From 9102a1910625b7f1e481257889cdaee9580ffe77 Mon Sep 17 00:00:00 2001 From: TalkingSasquach Date: Thu, 13 Oct 2022 16:45:01 -0400 Subject: [PATCH] Adding Tuning Fork, Removed Broken Chip8 --- applications/plugins/Tuning Fork/LICENSE | 674 ++++++++++++++++++ applications/plugins/Tuning Fork/README.md | 30 + .../plugins/Tuning Fork/application.fam | 14 + .../plugins/Tuning Fork/img/screenshot_1.png | Bin 0 -> 1638 bytes .../plugins/Tuning Fork/img/screenshot_2.png | Bin 0 -> 1874 bytes .../plugins/Tuning Fork/img/tuning_fork.gif | Bin 0 -> 90811 bytes applications/plugins/Tuning Fork/notes.h | 160 +++++ .../plugins/Tuning Fork/tuning_fork.c | 395 ++++++++++ .../plugins/Tuning Fork/tuning_fork_icon.png | Bin 0 -> 1187 bytes applications/plugins/Tuning Fork/tunings.h | 188 +++++ applications/plugins/chip8/application.fam | 0 applications/plugins/chip8/chip8.c | 203 ------ applications/plugins/chip8/chip8.h | 44 -- applications/plugins/chip8/chip8_app.c | 89 --- applications/plugins/chip8/chip8_app.h | 11 - applications/plugins/chip8/chip8_app_i.h | 33 - .../chip8/emulator_core/flipper_chip.c | 319 --------- .../chip8/emulator_core/flipper_chip.h | 74 -- .../chip8/emulator_core/flipper_fonts.h | 30 - .../plugins/chip8/scenes/chip8_scene.c | 26 - .../plugins/chip8/scenes/chip8_scene.h | 25 - .../plugins/chip8/scenes/chip8_scene_config.h | 2 - .../chip8/scenes/chip8_scene_file_select.c | 45 -- .../plugins/chip8/scenes/chip8_scene_work.c | 90 --- applications/plugins/chip8/views/chip8_view.c | 207 ------ applications/plugins/chip8/views/chip8_view.h | 29 - 26 files changed, 1461 insertions(+), 1227 deletions(-) create mode 100644 applications/plugins/Tuning Fork/LICENSE create mode 100644 applications/plugins/Tuning Fork/README.md create mode 100644 applications/plugins/Tuning Fork/application.fam create mode 100644 applications/plugins/Tuning Fork/img/screenshot_1.png create mode 100644 applications/plugins/Tuning Fork/img/screenshot_2.png create mode 100644 applications/plugins/Tuning Fork/img/tuning_fork.gif create mode 100644 applications/plugins/Tuning Fork/notes.h create mode 100644 applications/plugins/Tuning Fork/tuning_fork.c create mode 100644 applications/plugins/Tuning Fork/tuning_fork_icon.png create mode 100644 applications/plugins/Tuning Fork/tunings.h delete mode 100644 applications/plugins/chip8/application.fam delete mode 100644 applications/plugins/chip8/chip8.c delete mode 100644 applications/plugins/chip8/chip8.h delete mode 100644 applications/plugins/chip8/chip8_app.c delete mode 100644 applications/plugins/chip8/chip8_app.h delete mode 100644 applications/plugins/chip8/chip8_app_i.h delete mode 100644 applications/plugins/chip8/emulator_core/flipper_chip.c delete mode 100644 applications/plugins/chip8/emulator_core/flipper_chip.h delete mode 100644 applications/plugins/chip8/emulator_core/flipper_fonts.h delete mode 100644 applications/plugins/chip8/scenes/chip8_scene.c delete mode 100644 applications/plugins/chip8/scenes/chip8_scene.h delete mode 100644 applications/plugins/chip8/scenes/chip8_scene_config.h delete mode 100644 applications/plugins/chip8/scenes/chip8_scene_file_select.c delete mode 100644 applications/plugins/chip8/scenes/chip8_scene_work.c delete mode 100644 applications/plugins/chip8/views/chip8_view.c delete mode 100644 applications/plugins/chip8/views/chip8_view.h diff --git a/applications/plugins/Tuning Fork/LICENSE b/applications/plugins/Tuning Fork/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/plugins/Tuning Fork/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/plugins/Tuning Fork/README.md b/applications/plugins/Tuning Fork/README.md new file mode 100644 index 000000000..5524eba3e --- /dev/null +++ b/applications/plugins/Tuning Fork/README.md @@ -0,0 +1,30 @@ +# Tuning Fork + +Inspired by [Metronome](https://github.com/panki27/Metronome) + +A tuning fork for the [Flipper Zero](https://flipperzero.one/) device. +Allows to play different notes in different pitches. + +![screenshot](img/tuning_fork.gif) + +## Features +- Tuning forks (440Hz, 432Hz, etc.) +- Scientific pitch (..., 256Hz, 512Hz, 1024Hz, ...) +- Guitar Standard (6 strings) +- Guitar Drop D (6 strings) +- Guitar D (6 strings) +- Guitar Drop C (6 strings) +- Guitar Standard (7 strings) +- Bass Standard (4 strings) +- Bass Standard Tenor (4 strings) +- Bass Standard (5 strings) +- Bass Standard Tenor (5 strings) +- Bass Drop D (4 strings) +- Bass D (4 strings) +- Bass Drop A (5 strings) + +## Compiling + +``` +./fbt firmware_tuning_fork +``` diff --git a/applications/plugins/Tuning Fork/application.fam b/applications/plugins/Tuning Fork/application.fam new file mode 100644 index 000000000..e11930e54 --- /dev/null +++ b/applications/plugins/Tuning Fork/application.fam @@ -0,0 +1,14 @@ +App( + appid="tuning_fork", + name="Tuning Fork", + apptype=FlipperAppType.PLUGIN, + entry_point="tuning_fork_app", + cdefines=["APP_TUNING_FORM"], + requires=[ + "gui", + ], + fap_icon="tuning_fork_icon.png", + fap_category="Music", + stack_size=2 * 1024, + order=20, +) diff --git a/applications/plugins/Tuning Fork/img/screenshot_1.png b/applications/plugins/Tuning Fork/img/screenshot_1.png new file mode 100644 index 0000000000000000000000000000000000000000..04727988936235f3ba0760c472bd3cd0b5533b23 GIT binary patch literal 1638 zcmeH{Z&2EG9LGQC^qfU@c{h96&s-h<^0l zmR^_n=vnJwDA0Y7slL0}*qK|cNj|5Gk(^pC?I^tL71UpW?l(^`Y4qu7<9_rlfb}nz z#w=}-;0+D|@@@;Y!^4`HCQq7_>s=S9lK$2Qg!h7#k7L+Kk-8173e zNw)M5|Kftr&pnyc5dUw0^<6Pd4{SYuGb4*7~KVt0xR zRcAt7cx}I7(U&JfLS*5!vT=doP^7}6Y#_kN!YIaRJQQ?N0QSIl+eCN3K9Q?S!$eT= z(RI+8P@hjzNr^GZxl{9vyMz1?TT`O#9iqH!e1Lm=Wg)F!!P z)ZRa0ekdnSxH;;T!4b0!_gZkwb|qlwLTpb*ql^-F+C?NHrM?B>O^Df=yOXn98C_Y< z9PX}Va|^cSLg-R`pW^VAPV44viPR>Y1Zh4O(k{{1w9m7wuLe`Cyhj^l)W9f9;I5tW zgxCIRUgh;-qP0TX<*JD7p?dP2#0-fvIb-J9(k~+a#UD~be7$mAH^5&NPjfU%WavCH zpip(wRF9>tsty?TTH!dy-6+>)uUl9%zHTp5mBRgG)iW!KFtV&o2PGQ>fSWt5Ixv~? z+nT?fT~VuXr?aXBR4bdLa1!Z^`_hN`r}g$@#PUtw8(|W_7u%gjKi3x=xP{gwr-|yR zlE(F)3AFv$We|^e2qzFP)7UoOI{y_Lv;65!Jhw*_(ehb6_gm3IsRw0!}5i# YxFhu+FP?f~8MOf@I#x`VzFyPuHz~C-6aWAK literal 0 HcmV?d00001 diff --git a/applications/plugins/Tuning Fork/img/screenshot_2.png b/applications/plugins/Tuning Fork/img/screenshot_2.png new file mode 100644 index 0000000000000000000000000000000000000000..c31f37744ddcb463906f7c58313d030b4da86abe GIT binary patch literal 1874 zcmcIlYfuwc6uuD&NO@=(HBbd9)1uArFiHWV;sZs|()i*L3MkZK6H$Q@agmU5sBH%V zS{@1lK@lG$gc&pnSz>I9X0S>?LNXAeSSBtQlJE*41VTd_hjpg5f9#LlyLWf?obPkZ07F_ z*}OlaT&;)+SFFQf+t1(}`w|!C6%1F6-aqe}L!-)Qrwvl*3?(4N=hBxZb)b-rF~G99fI<4_t&RX=@O*CZo`f;iu!5d6 z#qri8nnXn|hv-~d@WVxF-==I@P_Sn|2IPDQz~%X1)5=6J!>$Xa$LuqXjQ$8EkeR7U z>MxTN(c_=BE*s`DE#81_CukeLcBI!EjJ&j$?!3#i{Hf~kqNCYkvcBTeP zjwQ9E=qTB_x5+b}kR=BOEc^_zueuPeYbhy9O;*D-5pkJxh*{4$O{XAN^|f_kz6PJ? zFnBNGsV(ZKN4QPj7fP47$7ZV2-6*Oyfiyij`8nhZf2CiQ&v5t+eg&dZMKh14`w}hnT2k>|ki#K*A=+$Y$b@Rb6Qf?ojcou zA#Ydze&1@@yas7|H%f$})eI(+D=s;D{cx6>6bWM78hFd2KSUi`&q&d59 z=$c%t7>*Ks_j#19-lKd_DX$N?TM!LnqaGnfRnhe8xe9N@h=61oL+6{4o72{BLh)Vv6~b^y*oK{>D%OClD)QqDSbu~w#`QmSEU>8^XfW~C}Cqgf~)QXoe#5de#~BKC77 f{&kFj6X#|?V&IG0Ysd|UX5Z}G79PUga`4Qb68W

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

LE0adHEt{4OVq>_g4OC$HHoWg5SeawC{1 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 literal 0 HcmV?d00001 diff --git a/applications/plugins/Tuning Fork/notes.h b/applications/plugins/Tuning Fork/notes.h new file mode 100644 index 000000000..dac0fbc88 --- /dev/null +++ b/applications/plugins/Tuning Fork/notes.h @@ -0,0 +1,160 @@ +#ifndef NOTES +#define NOTES + + +#define C0 16.35f +#define Cs0 17.32f +#define Db0 17.32f +#define D0 18.35f +#define Ds0 19.45f +#define Eb0 19.45f +#define E0 20.60f +#define F0 21.83f +#define Fs0 23.12f +#define Gb0 23.12f +#define G0 24.50f +#define Gs0 25.96f +#define Ab0 25.96f +#define A0 27.50f +#define As0 29.14f +#define Bb0 29.14f +#define B0 30.868f +#define C1 32.70f +#define Cs1 34.65f +#define Db1 34.65f +#define D1 36.71f +#define Ds1 38.89f +#define Eb1 38.89f +#define E1 41.203f +#define F1 43.65f +#define Fs1 46.25f +#define Gb1 46.25f +#define G1 49.00f +#define Gs1 51.91f +#define Ab1 51.91f +#define A1 55.00f +#define As1 58.27f +#define Bb1 58.27f +#define B1 61.74f +#define C2 65.41f +#define Cs2 69.30f +#define Db2 69.30f +#define D2 73.416f +#define Ds2 77.78f +#define Eb2 77.78f +#define E2 82.41f +#define F2 87.31f +#define Fs2 92.50f +#define Gb2 92.50f +#define G2 97.999f +#define Gs2 103.83f +#define Ab2 103.83f +#define A2 110.00f +#define As2 116.54f +#define Bb2 116.54f +#define B2 123.47f +#define C3 130.813f +#define Cs3 138.59f +#define Db3 138.59f +#define D3 146.83f +#define Ds3 155.56f +#define Eb3 155.56f +#define E3 164.81f +#define F3 174.61f +#define Fs3 185.00f +#define Gb3 185.00f +#define G3 196.00f +#define Gs3 207.65f +#define Ab3 207.65f +#define A3 220.00f +#define As3 233.08f +#define Bb3 233.08f +#define B3 246.94f +#define C4 261.63f +#define Cs4 277.18f +#define Db4 277.18f +#define D4 293.66f +#define Ds4 311.13f +#define Eb4 311.13f +#define E4 329.63f +#define F4 349.23f +#define Fs4 369.99f +#define Gb4 369.99f +#define G4 392.00f +#define Gs4 415.30f +#define Ab4 415.30f +#define A4 440.00f +#define As4 466.16f +#define Bb4 466.16f +#define B4 493.88f +#define C5 523.25f +#define Cs5 554.37f +#define Db5 554.37f +#define D5 587.33f +#define Ds5 622.25f +#define Eb5 622.25f +#define E5 659.25f +#define F5 698.46f +#define Fs5 739.99f +#define Gb5 739.99f +#define G5 783.99f +#define Gs5 830.61f +#define Ab5 830.61f +#define A5 880.00f +#define As5 932.33f +#define Bb5 932.33f +#define B5 987.77f +#define C6 1046.50f +#define Cs6 1108.73f +#define Db6 1108.73f +#define D6 1174.66f +#define Ds6 1244.51f +#define Eb6 1244.51f +#define E6 1318.51f +#define F6 1396.91f +#define Fs6 1479.98f +#define Gb6 1479.98f +#define G6 1567.98f +#define Gs6 1661.22f +#define Ab6 1661.22f +#define A6 1760.00f +#define As6 1864.66f +#define Bb6 1864.66f +#define B6 1975.53f +#define C7 2093.00f +#define Cs7 2217.46f +#define Db7 2217.46f +#define D7 2349.32f +#define Ds7 2489.02f +#define Eb7 2489.02f +#define E7 2637.02f +#define F7 2793.83f +#define Fs7 2959.96f +#define Gb7 2959.96f +#define G7 3135.96f +#define Gs7 3322.44f +#define Ab7 3322.44f +#define A7 3520.00f +#define As7 3729.31f +#define Bb7 3729.31f +#define B7 3951.07f +#define C8 4186.01f +#define Cs8 4434.92f +#define Db8 4434.92f +#define D8 4698.63f +#define Ds8 4978.03f +#define Eb8 4978.03f +#define E8 5274.04f +#define F8 5587.65f +#define Fs8 5919.91f +#define Gb8 5919.91f +#define G8 6271.93f +#define Gs8 6644.88f +#define Ab8 6644.88f +#define A8 7040.00f +#define As8 7458.62f +#define Bb8 7458.62f +#define B8 7902.13f + +#endif //NOTES + diff --git a/applications/plugins/Tuning Fork/tuning_fork.c b/applications/plugins/Tuning Fork/tuning_fork.c new file mode 100644 index 000000000..7cc2675b0 --- /dev/null +++ b/applications/plugins/Tuning Fork/tuning_fork.c @@ -0,0 +1,395 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "notes.h" +#include "tunings.h" + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +enum Page { + Tunings, + Notes +}; + +typedef struct { + bool playing; + enum Page page; + int current_tuning_note_index; + int current_tuning_index; + float volume; + TUNING tuning; +} TuningForkState; + +static TUNING current_tuning(TuningForkState* tuningForkState) { + return tuningForkState->tuning; +} + +static NOTE current_tuning_note(TuningForkState* tuningForkState) { + return current_tuning(tuningForkState).notes[tuningForkState->current_tuning_note_index]; +} + +static float current_tuning_note_freq(TuningForkState* tuningForkState) { + return current_tuning_note(tuningForkState).frequency; +} + +static void current_tuning_note_label(TuningForkState* tuningForkState, char* outNoteLabel) { + for(int i=0; i < 20; ++i){ + outNoteLabel[i] = current_tuning_note(tuningForkState).label[i]; + } +} + +static void current_tuning_label(TuningForkState* tuningForkState, char* outTuningLabel) { + for(int i=0; i < 20; ++i){ + outTuningLabel[i] = current_tuning(tuningForkState).label[i]; + } +} + +static void updateTuning(TuningForkState* tuning_fork_state) { + tuning_fork_state->tuning = TuningList[tuning_fork_state->current_tuning_index]; + tuning_fork_state->current_tuning_note_index = 0; +} + +static void next_tuning(TuningForkState* tuning_fork_state) { + if (tuning_fork_state->current_tuning_index == TUNINGS_COUNT - 1) { + tuning_fork_state->current_tuning_index = 0; + } else { + tuning_fork_state->current_tuning_index += 1; + } + updateTuning(tuning_fork_state); +} + +static void prev_tuning(TuningForkState* tuning_fork_state) { + if (tuning_fork_state->current_tuning_index - 1 < 0) { + tuning_fork_state->current_tuning_index = TUNINGS_COUNT - 1; + } else { + tuning_fork_state->current_tuning_index -= 1; + } + updateTuning(tuning_fork_state); +} + +static void next_note(TuningForkState* tuning_fork_state) { + if (tuning_fork_state->current_tuning_note_index == current_tuning(tuning_fork_state).notes_length - 1) { + tuning_fork_state->current_tuning_note_index = 0; + } else { + tuning_fork_state->current_tuning_note_index += 1; + } +} + +static void prev_note(TuningForkState* tuning_fork_state) { + if (tuning_fork_state->current_tuning_note_index == 0) { + tuning_fork_state->current_tuning_note_index = current_tuning(tuning_fork_state).notes_length - 1; + } else { + tuning_fork_state->current_tuning_note_index -= 1; + } +} + +static void increase_volume(TuningForkState* tuning_fork_state) { + if (tuning_fork_state->volume < 1.0f) { + tuning_fork_state->volume += 0.1f; + } +} + +static void decrease_volume(TuningForkState* tuning_fork_state) { + if (tuning_fork_state->volume > 0.0f) { + tuning_fork_state->volume -= 0.1f; + } +} + +static void play(TuningForkState* tuning_fork_state) { + furi_hal_speaker_start(current_tuning_note_freq(tuning_fork_state), tuning_fork_state->volume); +} + +static void stop() { + furi_hal_speaker_stop(); +} + +static void replay(TuningForkState* tuning_fork_state) { + stop(); + play(tuning_fork_state); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + TuningForkState* tuning_fork_state = acquire_mutex((ValueMutex*)ctx, 25); + if(tuning_fork_state == NULL) { + return; + } + + string_t tempStr; + string_init(tempStr); + + canvas_draw_frame(canvas, 0, 0, 128, 64); + + canvas_set_font(canvas, FontPrimary); + + if (tuning_fork_state->page == Tunings) { + char tuningLabel[20]; + current_tuning_label(tuning_fork_state, tuningLabel); + string_printf(tempStr, "< %s >", tuningLabel); + canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, string_get_cstr(tempStr)); + string_reset(tempStr); + } else { + char tuningLabel[20]; + current_tuning_label(tuning_fork_state, tuningLabel); + string_printf(tempStr, "%s", tuningLabel); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, string_get_cstr(tempStr)); + string_reset(tempStr); + + char tuningNoteLabel[20]; + current_tuning_note_label(tuning_fork_state, tuningNoteLabel); + string_printf(tempStr, "< %s >", tuningNoteLabel); + canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignCenter, string_get_cstr(tempStr)); + string_reset(tempStr); + } + + canvas_set_font(canvas, FontSecondary); + elements_button_left(canvas, "Prev"); + elements_button_right(canvas, "Next"); + + if (tuning_fork_state->page == Notes) { + if (tuning_fork_state->playing) { + elements_button_center(canvas, "Stop "); + } else { + elements_button_center(canvas, "Play"); + } + } else { + elements_button_center(canvas, "Select"); + } + if (tuning_fork_state->page == Notes) { + elements_progress_bar(canvas, 8, 36, 112, tuning_fork_state->volume); + } + + string_clear(tempStr); + release_mutex((ValueMutex*)ctx, tuning_fork_state); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void tuning_fork_state_init(TuningForkState* const tuning_fork_state) { + tuning_fork_state->playing = false; + tuning_fork_state->page = Tunings; + tuning_fork_state->volume = 1.0f; + tuning_fork_state->tuning = GuitarStandard6; + tuning_fork_state->current_tuning_index = 2; + tuning_fork_state->current_tuning_note_index = 0; +} + +int32_t tuning_fork_app() { + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + TuningForkState* tuning_fork_state = malloc(sizeof(TuningForkState)); + tuning_fork_state_init(tuning_fork_state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, tuning_fork_state, sizeof(TuningForkState))) { + FURI_LOG_E("TuningFork", "cannot create mutex\r\n"); + free(tuning_fork_state); + return 255; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + + TuningForkState* tuning_fork_state = (TuningForkState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeShort) { + // push events + switch(event.input.key) { + case InputKeyUp: + if (tuning_fork_state->page == Notes) { + increase_volume(tuning_fork_state); + if (tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyDown: + if (tuning_fork_state->page == Notes) { + decrease_volume(tuning_fork_state); + if (tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyRight: + if (tuning_fork_state->page == Tunings) { + next_tuning(tuning_fork_state); + } else { + next_note(tuning_fork_state); + if (tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyLeft: + if (tuning_fork_state->page == Tunings) { + prev_tuning(tuning_fork_state); + } else { + prev_note(tuning_fork_state); + if (tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyOk: + if (tuning_fork_state->page == Tunings) { + tuning_fork_state->page = Notes; + } else { + tuning_fork_state->playing = !tuning_fork_state->playing; + if (tuning_fork_state->playing) { + play(tuning_fork_state); + } else { + stop(); + } + } + break; + case InputKeyBack: + if (tuning_fork_state->page == Tunings) { + processing = false; + } else { + tuning_fork_state->playing = false; + tuning_fork_state->current_tuning_note_index = 0; + stop(); + tuning_fork_state->page = Tunings; + } + break; + } + } else if (event.input.type == InputTypeLong) { + // hold events + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + if (tuning_fork_state->page == Tunings) { + next_tuning(tuning_fork_state); + } else { + next_note(tuning_fork_state); + if (tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyLeft: + if (tuning_fork_state->page == Tunings) { + prev_tuning(tuning_fork_state); + } else { + prev_note(tuning_fork_state); + if (tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyOk: + break; + case InputKeyBack: + if (tuning_fork_state->page == Tunings) { + processing = false; + } else { + tuning_fork_state->playing = false; + stop(); + tuning_fork_state->page = Tunings; + tuning_fork_state->current_tuning_note_index = 0; + } + break; + } + } else if (event.input.type == InputTypeRepeat) { + // repeat events + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + if (tuning_fork_state->page == Tunings) { + next_tuning(tuning_fork_state); + } else { + next_note(tuning_fork_state); + if (tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyLeft: + if (tuning_fork_state->page == Tunings) { + prev_tuning(tuning_fork_state); + } else { + prev_note(tuning_fork_state); + if (tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyOk: + break; + case InputKeyBack: + if (tuning_fork_state->page == Tunings) { + processing = false; + } else { + tuning_fork_state->playing = false; + stop(); + tuning_fork_state->page = Tunings; + tuning_fork_state->current_tuning_note_index = 0; + } + break; + } + } + } + } else { + FURI_LOG_D("TuningFork", "FuriMessageQueue: event timeout"); + } + + view_port_update(view_port); + release_mutex(&state_mutex, tuning_fork_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + furi_record_close(RECORD_NOTIFICATION); + free(tuning_fork_state); + + return 0; +} diff --git a/applications/plugins/Tuning Fork/tuning_fork_icon.png b/applications/plugins/Tuning Fork/tuning_fork_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..074d9d5900e574dbac03b4e3785152c5e2cb0632 GIT binary patch literal 1187 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~$C3(BM zFfiWj5?%u2aTa()7Bet#3xP1>rMq>1fP(BLp1!W^&$xKGrB$O!lIj^47*|wxlq$-qD7Njav8u=Vft-Pbg>skKI`f8C{(J)v^; z{fpYh)>fAiK3#Tl`lM=67FW{Ck)nPoH0Jll5cAEO8-*lJhCVxRZ}N5S|JyQ5m%K2X zbLK%z^6njO4Li)bPOSEsl>5Q!QH$Zh9n*C==Ls2KTJ&bd4v9ux`K>Y9pW>KZ0}s07 zT{4PUvEl<;gFa85MWX1Tj@>TL1%>CjL@LRi%rd=tIB(BkdtHH=Zo|xANhYgEmYgZ%k`ZWGAR7I| z@k+*v^&iSpL@v!|wROxB`7-D5?U!ez^4vc!nDLI&OvaY!xYD=W`OVEYT6VC;FLf0? zov8jhq3GCGy(b+O!IwHNo6EmB%+vJD=wVE&-b=|Yt2gpZpH^5^X=X?iy=BZvtA3UH zU?caO=o@VBe9x?2yY=Q?-Jtv#+g$c*rn_I>IGOboFd7;2i~K?u&o3&U1>^{Ox;Tb# zTu-(rZeZlKwK?!XvWwyMBmw4G3qGX*rCU-XJkxxA8MJ_G4hB|6AqHlU2|&oeD8&Fu zH=s1-#V8GD2Qg|u)i5zIv}ZD~K-EM6X%O%LVvrsP&AfmSVd4TNxN5Ei%m_9}+X<(u z-++{AiEBhjN@7W>RdP`(kYX@0Ff!6LG|)9P4ly*eGBUF=G1fLPure_4?BSb^q9Hdw zB{QuOO@pDim5~WVgRf&kktouW<0pIuN(+E=1G6uR=UMqqTm;IA!Q?P~*T_3%Ertpd&&AC4)c~qs N@O1TaS?83{1OV5mj{N`t literal 0 HcmV?d00001 diff --git a/applications/plugins/Tuning Fork/tunings.h b/applications/plugins/Tuning Fork/tunings.h new file mode 100644 index 000000000..32d1b5e04 --- /dev/null +++ b/applications/plugins/Tuning Fork/tunings.h @@ -0,0 +1,188 @@ +#include "notes.h" + +#ifndef TUNINGS +#define TUNINGS + +typedef struct { + char label[20]; + float frequency; +} NOTE; + +typedef struct { + char label[20]; + int notes_length; + NOTE notes[20]; +} TUNING; + +const TUNING TuningForks = { + "Tuning forks", 6, { + { "Common A4 (440)", 440.00f}, + { "Sarti's A4 (436)", 436.00f}, + { "1858 A4 (435)", 435.00f}, + { "Verdi's A4 (432)", 432.00f}, + { "1750-1820 A4 (423.5)", 423.50f}, + { "Verdi's C4 (256.00)", 256.00f}, + } +}; + +const TUNING ScientificPitch = { + "Scientific pitch", 12, { + { "C0 (16Hz)", 16.0f}, + { "C1 (32Hz)", 32.0f}, + { "C2 (64Hz)", 64.0f}, + { "C3 (128Hz)", 128.0f}, + { "C4 (256Hz)", 256.0f}, + { "C5 (512Hz)", 512.0f}, + { "C6 (1024Hz)", 1024.0f}, + { "C7 (2048Hz)", 2048.0f}, + { "C8 (4096Hz)", 4096.0f}, + { "C9 (8192Hz)", 8192.0f}, + { "C10 (16384Hz)", 16384.0f}, + { "C11 (32768Hz)", 32768.0f} + } +}; + +const TUNING GuitarStandard6 = { + "Guitar Standard 6", 6, { + {"String 1", E4}, + {"String 2", B3}, + {"String 3", G3}, + {"String 4", D3}, + {"String 5", A2}, + {"String 6", E2} + } +}; + +const TUNING GuitarDropD6 = { + "Guitar Drop D 6", 6, { + {"String 1", E4}, + {"String 2", B3}, + {"String 3", G3}, + {"String 4", D3}, + {"String 5", A2}, + {"String 6", D2} + } +}; + +const TUNING GuitarD6 = { + "Guitar D 6", 6, { + {"String 1", D4}, + {"String 2", A3}, + {"String 3", F3}, + {"String 4", C3}, + {"String 5", G2}, + {"String 6", D2} + } +}; + +const TUNING GuitarDropC6 = { + "Guitar Drop C 6", 6, { + {"String 1", D4}, + {"String 2", A3}, + {"String 3", F3}, + {"String 4", C3}, + {"String 5", G2}, + {"String 6", C2} + } +}; + +const TUNING GuitarStandard7 = { + "Guitar Standard 7", 7, { + {"String 1", E4}, + {"String 2", B3}, + {"String 3", G3}, + {"String 4", D3}, + {"String 5", A2}, + {"String 6", E2}, + {"String 7", B1} + } +}; + +const TUNING BassStandard4 = { + "Bass Standard 4", 4, { + {"String 1", G2}, + {"String 2", D2}, + {"String 3", A1}, + {"String 4", E1} + } +}; + +const TUNING BassStandardTenor4 = { + "Bass Stand Tenor 4", 4, { + {"String 1", C3}, + {"String 2", G2}, + {"String 3", D2}, + {"String 4", A1} + } +}; + +const TUNING BassStandard5 = { + "Bass Standard 5", 5, { + {"String 1", G2}, + {"String 2", D2}, + {"String 3", A1}, + {"String 4", E1}, + {"String 5", B0} + } +}; + +const TUNING BassStandardTenor5 = { + "Bass Stand Tenor 5", 5, { + {"String 1", C3}, + {"String 2", G2}, + {"String 3", D2}, + {"String 4", A1}, + {"String 5", E1} + } +}; + +const TUNING BassDropD4 = { + "Bass Drop D 4", 4, { + {"String 1", G2}, + {"String 2", D2}, + {"String 3", A1}, + {"String 4", D1} + } +}; + +const TUNING BassD4 = { + "Bass D 4", 4, { + {"String 1", F2}, + {"String 2", C2}, + {"String 3", G1}, + {"String 4", D1} + } +}; + +const TUNING BassDropA5 = { + "Bass Drop A 5", 5, { + {"String 1", G2}, + {"String 2", D2}, + {"String 3", A1}, + {"String 4", E1}, + {"String 5", A0} + } +}; + +#define TUNINGS_COUNT 14 + +TUNING TuningList[TUNINGS_COUNT] = { + ScientificPitch, + TuningForks, + + GuitarStandard6, + GuitarDropD6, + GuitarD6, + GuitarDropC6, + GuitarStandard7, + + BassStandard4, + BassStandardTenor4, + BassStandard5, + BassStandardTenor5, + BassDropD4, + BassD4, + BassDropA5 +}; + +#endif //TUNINGS diff --git a/applications/plugins/chip8/application.fam b/applications/plugins/chip8/application.fam deleted file mode 100644 index e69de29bb..000000000 diff --git a/applications/plugins/chip8/chip8.c b/applications/plugins/chip8/chip8.c deleted file mode 100644 index 03591baf6..000000000 --- a/applications/plugins/chip8/chip8.c +++ /dev/null @@ -1,203 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "chip8.h" -#include "emulator_core/flipper_chip.h" - -#define TAG "Chip8Emulator" -#define WORKER_TAG TAG "Worker" -#define FILE_BUFFER_LEN 16 - -typedef enum { - WorkerEvtToggle = (1 << 1), - WorkerEvtEnd = (1 << 2), -} WorkerEvtFlags; - -struct Chip8Emulator { - Chip8State st; - FuriString* file_path; - FuriThread* thread; -}; - -static int32_t chip8_worker(void* context) { - Chip8Emulator* chip8 = context; - - FURI_LOG_I(WORKER_TAG, "Start furi record open"); - Storage* furi_storage_record = furi_record_open(RECORD_STORAGE); - FURI_LOG_I(WORKER_TAG, "furi record opened"); - - FURI_LOG_I(WORKER_TAG, "Start storage file alloc"); - File* rom_file = storage_file_alloc(furi_storage_record); - FURI_LOG_I( - WORKER_TAG, "Start storage file open, path = %s", furi_string_get_cstr(chip8->file_path)); - - uint8_t* rom_data = malloc(4096); - FURI_LOG_I(WORKER_TAG, "4096 array gotten"); - - while(1) { - if(chip8->st.worker_state == WorkerStateBackPressed) { - FURI_LOG_I(WORKER_TAG, "WorkerStateBackPressed"); - break; - } - - if(chip8->st.worker_state == WorkerStateLoadingRom) { - bool is_file_opened = storage_file_open( - rom_file, furi_string_get_cstr(chip8->file_path), FSAM_READ, FSOM_OPEN_EXISTING); - - if(!is_file_opened) { - FURI_LOG_I(WORKER_TAG, "Cannot open storage"); - storage_file_close(rom_file); - storage_file_free(rom_file); - chip8->st.worker_state = WorkerStateRomLoadError; - return 0; - } - - FURI_LOG_I(WORKER_TAG, "File was opened, try read this"); - - int rom_len = read_rom_data(rom_file, rom_data); - - FURI_LOG_I(WORKER_TAG, "Rom data finished reading"); - - FURI_LOG_I(WORKER_TAG, "Load chip8 core data"); - t_chip8_load_game(chip8->st.t_chip8_state, rom_data, rom_len); - FURI_LOG_I(WORKER_TAG, "chip8 core data loaded"); - - FURI_LOG_I(WORKER_TAG, "Wipe screen start"); - for(int i = 0; i < CHIP8_SCREEN_H; i++) { - FURI_LOG_I(WORKER_TAG, "Wipe screen line %d", i); - for(int j = 0; j < CHIP8_SCREEN_W; j++) { - chip8->st.t_chip8_state->screen[i][j] = 0; - } - furi_delay_ms(15); - } - FURI_LOG_I(WORKER_TAG, "Wipe screen end"); - - chip8->st.worker_state = WorkerStateRomLoaded; - } - - if(chip8->st.worker_state == WorkerStateRomLoaded) { - if(chip8->st.t_chip8_state->go_render) { - continue; - } - t_chip8_execute_next_opcode(chip8->st.t_chip8_state); - FURI_LOG_I( - "chip8_executing", - "current: 0x%X next: 0x%X", - chip8->st.t_chip8_state->current_opcode, - chip8->st.t_chip8_state->next_opcode); - furi_delay_ms(2); - //t_chip8_tick(chip8->st.t_chip8_state); - } - } - - FURI_LOG_I("CHIP8", "Prepare to ending app"); - storage_file_close(rom_file); - storage_file_free(rom_file); - t_chip8_free_memory(chip8->st.t_chip8_state, free); - FURI_LOG_I("CHIP8", "End ending"); - return 0; -} - -Chip8Emulator* chip8_make_emulator(FuriString* file_path) { - furi_assert(file_path); - FURI_LOG_I("CHIP8", "make emulator, file_path=", furi_string_get_cstr(file_path)); - - Chip8Emulator* chip8 = malloc(sizeof(Chip8Emulator)); - chip8->file_path = furi_string_alloc(); - string_set(chip8->file_path, file_path); - chip8->st.worker_state = WorkerStateLoadingRom; - chip8->st.t_chip8_state = t_chip8_init(malloc); - - // FURI_LOG_I(WORKER_TAG, "Start wipe screen"); - // furi_delay_ms(1500); - // for (int i = 0; i < CHIP8_SCREEN_H; i++) - // { - // FURI_LOG_I(WORKER_TAG, "Start wipe line %d", i); - // for (int j = 0; j < CHIP8_SCREEN_W; j++) - // { - // chip8->st.t_chip8_state->screen[i][j] = 0; - // } - // } - // FURI_LOG_I(WORKER_TAG, "End wipe screen"); - - chip8->thread = furi_thread_alloc(); - furi_thread_set_name(chip8->thread, "Chip8Worker"); - furi_thread_set_stack_size(chip8->thread, 4096); - furi_thread_set_context(chip8->thread, chip8); - furi_thread_set_callback(chip8->thread, chip8_worker); - - furi_thread_start(chip8->thread); - return chip8; -} - -void chip8_close_emulator(Chip8Emulator* chip8) { - FURI_LOG_I("chip_8_close_emulator", "start"); - furi_assert(chip8); - furi_thread_flags_set(furi_thread_get_id(chip8->thread), WorkerEvtEnd); - furi_thread_join(chip8->thread); - furi_thread_free(chip8->thread); - furi_string_free(chip8->file_path); - free(chip8); - FURI_LOG_I("chip_8_close_emulator", "end"); -} - -void chip8_toggle(Chip8Emulator* chip8) { - furi_assert(chip8); - furi_thread_flags_set(furi_thread_get_id(chip8->thread), WorkerEvtToggle); -} - -Chip8State* chip8_get_state(Chip8Emulator* chip8) { - furi_assert(chip8); - return &(chip8->st); -} - -uint16_t read_rom_data(File* file, uint8_t* data) { - furi_assert(file); - furi_assert(data); - - const uint8_t buffer_size = 32; - uint16_t file_pointer = 0; - uint8_t buff[buffer_size]; - - while(1) { - uint16_t bytes_were_read = storage_file_read(file, buff, buffer_size); - - if(bytes_were_read == 0) { - break; - } - - for(uint16_t i = 0; i < bytes_were_read; i++) { - data[file_pointer] = buff[i]; - file_pointer++; - } - } - - return file_pointer; -} - -void chip8_set_back_pressed(Chip8Emulator* chip8) { - chip8->st.worker_state = WorkerStateBackPressed; - chip8->st.t_chip8_state->go_render = true; - FURI_LOG_I(WORKER_TAG, "SET BACK PRESSED. EMULATION IS STOPPED"); -} - -void chip8_set_up_pressed(Chip8Emulator* chip8) { - chip8->st.t_chip8_state->go_render = true; - t_chip8_set_input(chip8->st.t_chip8_state, k_1); - FURI_LOG_I(WORKER_TAG, "UP PRESSED"); -} - -void chip8_set_down_pressed(Chip8Emulator* chip8) { - chip8->st.t_chip8_state->go_render = true; - t_chip8_set_input(chip8->st.t_chip8_state, k_4); - FURI_LOG_I(WORKER_TAG, "DOWN PRESSED"); -} - -void chip8_release_keyboard(Chip8Emulator* chip8) { - chip8->st.t_chip8_state->go_render = true; - t_chip8_release_input(chip8->st.t_chip8_state); - FURI_LOG_I(WORKER_TAG, "chip8_release_keyboard Release input"); -} diff --git a/applications/plugins/chip8/chip8.h b/applications/plugins/chip8/chip8.h deleted file mode 100644 index f3864201a..000000000 --- a/applications/plugins/chip8/chip8.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include "emulator_core/flipper_chip.h" - -#define CHIP8_SCREEN_W 64 -#define CHIP8_SCREEN_H 32 - -typedef struct Chip8Emulator Chip8Emulator; - -typedef enum { - WorkerStateLoadingRom, - WorkerStateRomLoaded, - WorkerStateRomLoadError, - WorkerStateBackPressed, -} WorkerState; - -typedef struct { - WorkerState worker_state; - t_chip8_state* t_chip8_state; -} Chip8State; - -Chip8Emulator* chip8_make_emulator(FuriString* file_path); - -void chip8_close_emulator(Chip8Emulator* chip8); -void chip8_set_back_pressed(Chip8Emulator* chip8); -void chip8_set_up_pressed(Chip8Emulator* chip8); -void chip8_set_down_pressed(Chip8Emulator* chip8); -void chip8_release_keyboard(Chip8Emulator* chip8); - -Chip8State* chip8_get_state(Chip8Emulator* chip8); - -void chip8_toggle(Chip8Emulator* chip8); - -uint16_t read_rom_data(File* file, uint8_t* data); - -#ifdef __cplusplus -} -#endif diff --git a/applications/plugins/chip8/chip8_app.c b/applications/plugins/chip8/chip8_app.c deleted file mode 100644 index 1b4d06646..000000000 --- a/applications/plugins/chip8/chip8_app.c +++ /dev/null @@ -1,89 +0,0 @@ -#include "chip8_app_i.h" -#include - -static bool chip8_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - Chip8App* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool chip8_app_back_event_callback(void* context) { - furi_assert(context); - Chip8App* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void chip8_app_tick_event_callback(void* context) { - furi_assert(context); - Chip8App* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -uint8_t** chip8_backup_screen_alloc() { - FURI_LOG_I("chip8", "chip8_backup_screen_alloc start"); - - uint8_t** backup_screen = malloc(SCREEN_HEIGHT * sizeof(size_t)); - for(int i = 0; i < SCREEN_HEIGHT; i++) { - backup_screen[i] = malloc(SCREEN_WIDTH * sizeof(uint8_t)); - for(int j = 0; j < SCREEN_WIDTH; j++) { - backup_screen[i][j] = 0; - } - } - - FURI_LOG_I("chip8", "chip8_backup_screen_alloc end"); - return backup_screen; -} - -Chip8App* chip8_app_alloc() { - Chip8App* app = malloc(sizeof(Chip8App)); - - app->gui = furi_record_open(RECORD_GUI); - app->dialogs = furi_record_open(RECORD_DIALOGS); - app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&chip8_scene_handlers, app); - - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, chip8_app_tick_event_callback, 100); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, chip8_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, chip8_app_back_event_callback); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - app->chip8_view = chip8_alloc(); - app->backup_screen = chip8_backup_screen_alloc(); - view_dispatcher_add_view(app->view_dispatcher, Chip8WorkView, chip8_get_view(app->chip8_view)); - - scene_manager_next_scene(app->scene_manager, Chip8FileSelectView); - - return app; -} - -void chip8_app_free(Chip8App* app) { - FURI_LOG_I("CHIP8", "chip8_app_free started"); - furi_assert(app); - - view_dispatcher_remove_view(app->view_dispatcher, Chip8FileSelectView); - view_dispatcher_remove_view(app->view_dispatcher, Chip8WorkView); - chip8_free(app->chip8_view); - - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_DIALOGS); - - free(app); -} - -int32_t chip8_app(void* p) { - Chip8App* chip8_app = chip8_app_alloc(); - - view_dispatcher_run(chip8_app->view_dispatcher); - chip8_app_free(chip8_app); - return 0; -} \ No newline at end of file diff --git a/applications/plugins/chip8/chip8_app.h b/applications/plugins/chip8/chip8_app.h deleted file mode 100644 index 0d5720388..000000000 --- a/applications/plugins/chip8/chip8_app.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct Chip8App Chip8App; - -#ifdef __cplusplus -} -#endif diff --git a/applications/plugins/chip8/chip8_app_i.h b/applications/plugins/chip8/chip8_app_i.h deleted file mode 100644 index 8d8b6bc27..000000000 --- a/applications/plugins/chip8/chip8_app_i.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "chip8_app.h" -#include "scenes/chip8_scene.h" -#include "chip8.h" - -#include -#include -#include -#include -#include -#include -#include "views/chip8_view.h" - -#define CHIP8_APP_PATH_FOLDER "/any/chip8" -#define CHIP8_APP_EXTENSION ".ch8" - -struct Chip8App { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - DialogsApp* dialogs; - - FuriString* file_name; - uint8_t** backup_screen; - Chip8View* chip8_view; - Chip8Emulator* chip8; -}; - -typedef enum { - Chip8FileSelectView, - Chip8WorkView, -} Chip8AppView; diff --git a/applications/plugins/chip8/emulator_core/flipper_chip.c b/applications/plugins/chip8/emulator_core/flipper_chip.c deleted file mode 100644 index 4b438cac2..000000000 --- a/applications/plugins/chip8/emulator_core/flipper_chip.c +++ /dev/null @@ -1,319 +0,0 @@ -// -// Created by dwdraugr on 24.11.2021. -// - -#include "flipper_chip.h" -#include "flipper_fonts.h" -#include -#include -#include -#include - -static uint8_t randbyte(); -static void draw_sprite(t_chip8_state* state, uint8_t x, uint8_t y, uint8_t n); -static void error_stop(t_chip8_state* state, uint16_t opcode); - -t_chip8_state* t_chip8_init(void* (*system_malloc)(size_t)) { - t_chip8_state* state = system_malloc(sizeof(t_chip8_state)); - - state->PC = MEMORY_START_POSITION; - state->SP = 0; - state->I = 0; - state->delay_timer = 0; - state->sound_timer = 0; - state->go_render = false; - state->next_opcode = 0; - - state->memory = system_malloc(MEMORY_SIZE * sizeof(uint8_t)); - // memset(state->memory, 0, MEMORY_SIZE); - state->screen = system_malloc(SCREEN_HEIGHT * sizeof(size_t)); - for(int i = 0; i < SCREEN_HEIGHT; i++) { - state->screen[i] = system_malloc(SCREEN_WIDTH * sizeof(uint8_t)); - // memset(state->screen[i], 0, SCREEN_WIDTH); - } - state->V = system_malloc(CPU_REGISTER_NUMBER * sizeof(uint8_t)); - // memset(state->V, 0, CPU_REGISTER_NUMBER); - state->stack = system_malloc(CPU_STACK_DEPTH * sizeof(uint16_t)); - // memset(state->stack, 0, CPU_STACK_DEPTH * sizeof(short)); - state->key = system_malloc(KEYS_NUMBER * sizeof(uint8_t)); - // memset(state->key, 0, KEYS_NUMBER); - - memcpy(state->memory, font_small, FONT_SMALL); - srand(time(NULL)); - - return state; -} - -bool t_chip8_load_game(t_chip8_state* state, const uint8_t* rom, int rom_size) { - if(MEMORY_ROM_SIZE < rom_size) { - return false; - } - memcpy(&state->memory[MEMORY_START_POSITION], rom, rom_size); - return true; -} - -void t_chip8_free_memory(t_chip8_state* state, void (*system_free)(void*)) { - system_free(state->memory); - for(int i = 0; i < SCREEN_HEIGHT; i++) { - system_free(state->screen[i]); - } - system_free(state->screen); - system_free(state->V); - system_free(state->key); - system_free(state->stack); - system_free(state); -} - -void t_chip8_execute_next_opcode(t_chip8_state* state) { - static bool isWaitInput = false; - static uint8_t register_number = 255; - - uint16_t opcode = state->memory[state->PC] << 8 | state->memory[state->PC + 1]; - uint8_t x = (opcode >> 8) & 0x000F; - uint8_t y = (opcode >> 4) & 0x000F; - uint8_t n = opcode & 0x000F; - uint8_t kk = opcode & 0x00FF; - uint16_t nnn = opcode & 0x0FFF; - - // jump to input-wait opcode - if(isWaitInput) { - opcode = 0xF000; - kk = 0x0A; - x = register_number; - } - state->current_opcode = opcode & 0xF000; - switch(opcode & 0xF000) { - case 0x0000: - switch(kk) { - case 0x00E0: - for(int i = 0; i < SCREEN_HEIGHT; i++) { - for(int j = 0; j < SCREEN_WIDTH; j++) { - state->screen[i][j] = 0; - } - } - state->PC += 2; - break; - case 0x00EE: - state->PC = state->stack[--state->SP]; - break; - default: - error_stop(state, opcode); - } - break; - case 0x1000: - state->PC = nnn; - break; - case 0x2000: - state->stack[state->SP++] = state->PC + 2; - state->PC = nnn; - break; - case 0x3000: - state->PC += (state->V[x] == kk) ? 4 : 2; - break; - case 0x4000: - state->PC += (state->V[x] != kk) ? 4 : 2; - break; - case 0x5000: - state->PC += (state->V[x] == state->V[y]) ? 4 : 2; - break; - case 0x6000: - state->V[x] = kk; - state->PC += 2; - break; - case 0x7000: - state->V[x] += kk; - state->PC += 2; - break; - case 0x8000: - switch(n) { - case 0x0: - state->V[x] = state->V[y]; - break; - case 0x1: - state->V[x] |= state->V[y]; - break; - case 0x2: - state->V[x] &= state->V[y]; - break; - case 0x3: - state->V[x] ^= state->V[y]; - break; - case 0x4: - state->V[0xF] = (int)state->V[x] + (int)state->V[y] ? 1 : 0; - state->V[x] += state->V[y]; - break; - case 0x5: - state->V[0xF] = state->V[x] > state->V[y] ? 1 : 0; - state->V[x] -= state->V[y]; - break; - case 0x6: - state->V[0xF] = state->V[x] & 0x1; - state->V[x] >>= 1; - break; - case 0x7: - state->V[0xF] = state->V[y] > state->V[x] ? 1 : 0; - state->V[x] = state->V[y] - state->V[x]; - break; - case 0xE: - state->V[0xF] = (state->V[x] >> 7) & 0x1; - state->V[x] <<= 1; - break; - default: - error_stop(state, opcode); - } - state->PC += 2; - break; - case 0x9000: - switch(n) { - case 0x0: - state->PC += state->V[x] != state->V[y] ? 4 : 2; - break; - default: - error_stop(state, opcode); - } - break; - case 0xA000: - state->I = nnn; - state->PC += 2; - break; - case 0xB000: - state->PC = nnn + state->V[0]; - break; - case 0xC000: - state->V[x] = randbyte() & kk; - state->PC += 2; - break; - case 0xD000: - draw_sprite(state, state->V[x], state->V[y], n); - state->go_render = true; - state->PC += 2; - break; - case 0xE000: - switch(kk) { - case 0x9E: - state->PC += state->key[state->V[x]] ? 4 : 2; - break; - case 0xA1: - state->PC += !state->key[state->V[x]] ? 4 : 2; - break; - default: - error_stop(state, opcode); - } - break; - case 0xF000: - switch(kk) { - case 0x07: - state->V[x] = state->delay_timer; - state->PC += 2; - break; - case 0x0A: - for(int i = 0; i < KEYS_NUMBER; i++) { - if(state->key[i]) { - state->V[x] = i; - isWaitInput = false; - goto exit_input_wait; - } - isWaitInput = true; - register_number = x; - } - exit_input_wait: - state->PC += 2; - break; - case 0x15: - state->delay_timer = state->V[x]; - state->PC += 2; - break; - case 0x18: - state->sound_timer = state->V[x]; - state->PC += 2; - break; - case 0x1E: - state->V[0xF] = state->I + state->V[x] > 0xFFF ? 1 : 0; - state->I += state->V[x]; - state->PC += 2; - break; - case 0x29: - state->I = FONT_BYTES_PER_CHAR * state->V[x]; - state->PC += 2; - break; - case 0x33: - state->memory[state->I] = (state->V[x] % 1000) / 100; - state->memory[state->I + 1] = (state->V[x] % 100) / 10; - state->memory[state->I + 2] = state->V[x] % 10; - state->PC += 2; - break; - case 0x55: - memcpy(state->memory, state->V, x); - state->I += x + 1; - state->PC += 2; - break; - case 0x65: - for(int i = 0; i <= x; i++) { - state->V[i] = state->memory[state->I + i]; - } - state->I += x + 1; - state->PC += 2; - break; - default: - error_stop(state, opcode); - } - break; - default: - error_stop(state, opcode); - } - - state->next_opcode = state->memory[state->PC] << 8 | state->memory[state->PC + 1]; - state->next_opcode &= 0xf000; -} - -void t_chip8_tick(t_chip8_state* state) { - if(state->delay_timer > 0) { - --state->delay_timer; - } - if(state->sound_timer > 0) { - --state->sound_timer; - } -} - -uint8_t** t_chip8_get_screen(t_chip8_state* state) { - return (uint8_t**)state->screen; -} - -void t_chip8_set_input(t_chip8_state* state, t_keys key) { - state->key[key] = 1; -} - -void t_chip8_release_input(t_chip8_state* state) { - for(int i = 0; i < KEYS_NUMBER; i++) { - state->key[i] = 0; - } -} - -static uint8_t randbyte() { - return rand() % 256; -} - -static void draw_sprite(t_chip8_state* state, uint8_t x, uint8_t y, uint8_t n) { - unsigned row = y, col = x; - unsigned byte_index; - unsigned bit_index; - - state->V[0xF] = 0; - for(byte_index = 0; byte_index < n; byte_index++) { - uint8_t byte = state->memory[state->I + byte_index]; - - for(bit_index = 0; bit_index < 8; bit_index++) { - uint8_t bit = (byte >> bit_index) & 0x1; - - uint8_t* pixel_pointer = &state->screen[(row + byte_index) % SCREEN_HEIGHT] - [(col + (7 - bit_index)) % SCREEN_WIDTH]; - - if(bit == 1 && *pixel_pointer == 1) state->V[0xF] = 1; - *pixel_pointer = *pixel_pointer ^ bit; - } - } -} - -static void error_stop(t_chip8_state* state, uint16_t opcode) { - exit(100); -} diff --git a/applications/plugins/chip8/emulator_core/flipper_chip.h b/applications/plugins/chip8/emulator_core/flipper_chip.h deleted file mode 100644 index 09c2ed298..000000000 --- a/applications/plugins/chip8/emulator_core/flipper_chip.h +++ /dev/null @@ -1,74 +0,0 @@ -// -// Created by dwdraugr on 24.11.2021. -// - -#ifndef FLIPPER_CHIP_FLIPPER_CHIP_H -#define FLIPPER_CHIP_FLIPPER_CHIP_H - -#include -#include -#include - -#define CPU_REGISTER_NUMBER 0x10 -#define CPU_STACK_DEPTH 0x10 - -#define MEMORY_SIZE 0x1000 -#define MEMORY_START_POSITION 0x200 -#define MEMORY_ROM_SIZE (MEMORY_SIZE - MEMORY_START_POSITION) - -#define SCREEN_WIDTH 64 -#define SCREEN_HEIGHT 32 -#define SCREEN_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT) - -#define KEYS_NUMBER 0x10 - -typedef struct s_flipper_state { - uint16_t I; - uint16_t PC; - uint8_t SP; - uint8_t* V; // CPU_REGISTER_NUMBER - uint16_t* stack; // CPU_STACK_DEPTH - uint8_t* memory; // MEMORY_SIZE - uint8_t delay_timer; - uint8_t sound_timer; - uint8_t** screen; // SCREEN_HEIGHT * SCREEN_WIDTH - uint8_t* key; // KEYS_NUMBER - bool go_render; - int current_opcode; - int next_opcode; -} t_chip8_state; - -// keyboard layout -// 1 2 3 C -// 4 5 6 D -// 7 8 9 E -// A 0 B F -typedef enum e_keys { - k_x, - k_1, - k_2, - k_3, - k_q, - k_w, - k_e, - k_a, - k_s, - k_d, - k_z, - k_c, - k_4, - k_r, - k_f, - k_v, -} t_keys; - -t_chip8_state* t_chip8_init(void* (*system_malloc)(size_t)); -bool t_chip8_load_game(t_chip8_state* state, const uint8_t* rom, int rom_size); -void t_chip8_execute_next_opcode(t_chip8_state* state); -void t_chip8_tick(t_chip8_state* state); -uint8_t** t_chip8_get_screen(t_chip8_state* state); -void t_chip8_free_memory(t_chip8_state* state, void (*system_free)(void*)); -void t_chip8_set_input(t_chip8_state* state, t_keys key); -void t_chip8_release_input(t_chip8_state* state); - -#endif //FLIPPER_CHIP_FLIPPER_CHIP_H diff --git a/applications/plugins/chip8/emulator_core/flipper_fonts.h b/applications/plugins/chip8/emulator_core/flipper_fonts.h deleted file mode 100644 index f1bcec1b9..000000000 --- a/applications/plugins/chip8/emulator_core/flipper_fonts.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// Created by dwdraugr on 24.11.2021. -// - -#ifndef FLIPPER_CHIP_FLIPPER_FONTS_H -#define FLIPPER_CHIP_FLIPPER_FONTS_H - -#define FONT_BYTES_PER_CHAR 5 -#define FONT_SMALL (16 * 5) - -unsigned char font_small[FONT_SMALL] = { - 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0 - 0x20, 0x60, 0x20, 0x20, 0x70, // 1 - 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2 - 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3 - 0x90, 0x90, 0xF0, 0x10, 0x10, // 4 - 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5 - 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6 - 0xF0, 0x10, 0x20, 0x40, 0x40, // 7 - 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8 - 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9 - 0xF0, 0x90, 0xF0, 0x90, 0x90, // A - 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B - 0xF0, 0x80, 0x80, 0x80, 0xF0, // C - 0xE0, 0x90, 0x90, 0x90, 0xE0, // D - 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E - 0xF0, 0x80, 0xF0, 0x80, 0x80 // F -}; - -#endif //FLIPPER_CHIP_FLIPPER_FONTS_H diff --git a/applications/plugins/chip8/scenes/chip8_scene.c b/applications/plugins/chip8/scenes/chip8_scene.c deleted file mode 100644 index 34488c888..000000000 --- a/applications/plugins/chip8/scenes/chip8_scene.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "chip8_scene.h" - -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const chip8_scene_on_enter_handlers[])(void*) = { -#include "chip8_scene_config.h" -}; -#undef ADD_SCENE - -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const chip8_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "chip8_scene_config.h" -}; -#undef ADD_SCENE - -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const chip8_scene_on_exit_handlers[])(void* context) = { -#include "chip8_scene_config.h" -}; -#undef ADD_SCENE - -const SceneManagerHandlers chip8_scene_handlers = { - .on_enter_handlers = chip8_scene_on_enter_handlers, - .on_event_handlers = chip8_scene_on_event_handlers, - .on_exit_handlers = chip8_scene_on_exit_handlers, - .scene_num = Chip8SceneNum, -}; diff --git a/applications/plugins/chip8/scenes/chip8_scene.h b/applications/plugins/chip8/scenes/chip8_scene.h deleted file mode 100644 index 19fd40c62..000000000 --- a/applications/plugins/chip8/scenes/chip8_scene.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -#define ADD_SCENE(prefix, name, id) Chip8Scene##id, -typedef enum { -#include "chip8_scene_config.h" - Chip8SceneNum, -} Chip8Scene; -#undef ADD_SCENE - -extern const SceneManagerHandlers chip8_scene_handlers; - -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "chip8_scene_config.h" -#undef ADD_SCENE - -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "chip8_scene_config.h" -#undef ADD_SCENE - -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "chip8_scene_config.h" -#undef ADD_SCENE diff --git a/applications/plugins/chip8/scenes/chip8_scene_config.h b/applications/plugins/chip8/scenes/chip8_scene_config.h deleted file mode 100644 index 593249a87..000000000 --- a/applications/plugins/chip8/scenes/chip8_scene_config.h +++ /dev/null @@ -1,2 +0,0 @@ -ADD_SCENE(chip8, file_select, FileSelect) -ADD_SCENE(chip8, work, Work) \ No newline at end of file diff --git a/applications/plugins/chip8/scenes/chip8_scene_file_select.c b/applications/plugins/chip8/scenes/chip8_scene_file_select.c deleted file mode 100644 index 246e62170..000000000 --- a/applications/plugins/chip8/scenes/chip8_scene_file_select.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "../chip8_app_i.h" -#include "furi_hal_power.h" - -static bool chip8_file_select(Chip8App* chip8) { - furi_assert(chip8); - chip8->file_name = furi_string_alloc(); - furi_string_set(chip8->file_name, CHIP8_APP_PATH_FOLDER); - // furi_string_set(file_path, chip8->file_name); - - bool res = dialog_file_browser_show( - chip8->dialogs, - chip8->file_name, - chip8->file_name, - CHIP8_APP_EXTENSION, - true, - &I_unknown_10px, - false); - - FURI_LOG_I( - "Chip8_file_browser_show", "chip8->file_name: %s", furi_string_get_cstr(chip8->file_name)); - FURI_LOG_I("Chip8_file_browser_show", "res: %d", res); - return res; -} - -void chip8_scene_file_select_on_enter(void* context) { - Chip8App* chip8 = context; - - if(chip8_file_select(chip8)) { - FURI_LOG_I( - "Chip8", "chip8_file_select, file_name = %s", furi_string_get_cstr(chip8->file_name)); - scene_manager_next_scene(chip8->scene_manager, Chip8WorkView); - } else { - view_dispatcher_stop(chip8->view_dispatcher); - } -} - -bool chip8_scene_file_select_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void chip8_scene_file_select_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/plugins/chip8/scenes/chip8_scene_work.c b/applications/plugins/chip8/scenes/chip8_scene_work.c deleted file mode 100644 index 4cb6bce32..000000000 --- a/applications/plugins/chip8/scenes/chip8_scene_work.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "../chip8_app_i.h" -#include "../views/chip8_view.h" -#include "furi_hal.h" - -void chip8_scene_ok_callback(InputType type, void* context) { - furi_assert(context); - Chip8App* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, type); -} - -void chip8_scene_back_callback(Chip8View* view, InputType type, void* context) { - furi_assert(context); - Chip8App* app = context; - chip8_set_back_pressed(app->chip8); - chip8_set_state(view, chip8_get_state(app->chip8)); - // view_dispatcher_send_custom_event(app->view_dispatcher, type); - view_dispatcher_stop(app->view_dispatcher); -} - -void chip8_scene_up_callback(InputType type, void* context) { - furi_assert(context); - Chip8App* app = context; - chip8_set_up_pressed(app->chip8); - // view_dispatcher_send_custom_event(app->view_dispatcher, type); -} - -void chip8_scene_down_callback(InputType type, void* context) { - furi_assert(context); - Chip8App* app = context; - chip8_set_down_pressed(app->chip8); - // view_dispatcher_send_custom_event(app->view_dispatcher, type); -} - -void chip8_scene_release_callback(InputType type, void* context) { - furi_assert(context); - Chip8App* app = context; - chip8_release_keyboard(app->chip8); - // view_dispatcher_send_custom_event(app->view_dispatcher, type); -} - -bool chip8_scene_work_on_event(void* context, SceneManagerEvent event) { - Chip8App* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - chip8_toggle(app->chip8); - consumed = true; - } else if(event.type == SceneManagerEventTypeTick) { - Chip8State* state = chip8_get_state(app->chip8); - - chip8_set_state(app->chip8_view, state); - } - return consumed; -} - -void chip8_scene_work_on_enter(void* context) { - Chip8App* app = context; - - chip8_set_file_name(app->chip8_view, app->file_name); - - FuriString* file_tmp; - file_tmp = furi_string_alloc(); - - furi_string_printf(file_tmp, "%s", furi_string_get_cstr(app->file_name)); - - FURI_LOG_I("chip8_scene_work_on_enter", "file_name: %s", furi_string_get_cstr(file_tmp)); - - FURI_LOG_I("chip8_scene_work_on_enter", "START SET BACKUP SCREEN"); - chip8_set_backup_screen(app->chip8_view, app->backup_screen); - FURI_LOG_I("chip8_scene_work_on_enter", "END SET BACKUP SCREEN"); - - app->chip8 = chip8_make_emulator(file_tmp); - - furi_string_free(file_tmp); - - chip8_set_state(app->chip8_view, chip8_get_state(app->chip8)); - - chip8_set_ok_callback(app->chip8_view, chip8_scene_ok_callback, app); - chip8_set_back_callback(app->chip8_view, chip8_scene_back_callback, app); - chip8_set_up_callback(app->chip8_view, chip8_scene_up_callback, app); - chip8_set_down_callback(app->chip8_view, chip8_scene_down_callback, app); - chip8_set_release_callback(app->chip8_view, chip8_scene_release_callback, app); - - view_dispatcher_switch_to_view(app->view_dispatcher, Chip8WorkView); -} - -void chip8_scene_work_on_exit(void* context) { - Chip8App* app = context; - chip8_close_emulator(app->chip8); -} diff --git a/applications/plugins/chip8/views/chip8_view.c b/applications/plugins/chip8/views/chip8_view.c deleted file mode 100644 index ca741cde2..000000000 --- a/applications/plugins/chip8/views/chip8_view.c +++ /dev/null @@ -1,207 +0,0 @@ -#include -#include "chip8_view.h" -#include "../chip8.h" -#include "../emulator_core/flipper_chip.h" - -struct Chip8View { - View* view; - Chip8ViewCallback callback; - void* context; - Chip8ViewKeyBackCallback backCallback; - Chip8ViewKeyUpCallback upCallback; - Chip8ViewKeyDownCallback downCallback; - Chip8ViewReleaseCallback releaseCallback; -}; - -typedef struct { - FuriString* file_name; - Chip8State state; - uint8_t** backup_screen; -} Chip8Model; - -static void chip8_draw_callback(Canvas* canvas, void* _model) { - Chip8Model* model = _model; - - if(model->state.worker_state == WorkerStateLoadingRom) { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); - } - - if(model->state.worker_state == WorkerStateRomLoaded) { - while(!model->state.t_chip8_state->go_render) { - for(int y = 0; y < CHIP8_SCREEN_H; y++) { - for(int x = 0; x < CHIP8_SCREEN_W; x++) { - if(model->backup_screen[y][x] == 0) { - canvas_set_color(canvas, ColorWhite); - } else { - canvas_set_color(canvas, ColorBlack); - } - canvas_draw_box(canvas, x * 2, y * 2, 2, 2); - //canvas_draw_dot(canvas, x, y); - } - } - return; - } - - uint8_t** screen = t_chip8_get_screen(model->state.t_chip8_state); - - for(int y = 0; y < CHIP8_SCREEN_H; y++) { - for(int x = 0; x < CHIP8_SCREEN_W; x++) { - if(screen[y][x] == 0) { - canvas_set_color(canvas, ColorWhite); - } else { - canvas_set_color(canvas, ColorBlack); - } - canvas_draw_box(canvas, x * 2, y * 2, 2, 2); - model->backup_screen[y][x] = screen[y][x]; - //canvas_draw_dot(canvas, x, y); - } - } - model->state.t_chip8_state->go_render = false; - } - - if(model->state.worker_state == WorkerStateRomLoadError) { - canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); - } -} - -static bool chip8_input_callback(InputEvent* event, void* context) { - FURI_LOG_I("Chip8", "received input"); - furi_assert(context); - Chip8View* chip8 = context; - bool consumed = false; - - if(event->type == InputTypeShort) { - if(event->key == InputKeyOk) { - consumed = true; - furi_assert(chip8->callback); - chip8->callback(InputTypeShort, chip8->context); - } - - if(event->key == InputKeyBack) { - consumed = true; - furi_assert(chip8->callback); - chip8->backCallback(chip8, InputTypeShort, chip8->context); - } - - if(event->key == InputKeyUp) { - consumed = true; - furi_assert(chip8->upCallback); - chip8->upCallback(InputTypeShort, chip8->context); - } - if(event->key == InputKeyDown) { - consumed = true; - furi_assert(chip8->downCallback); - chip8->downCallback(InputTypeShort, chip8->context); - } - } - if(event->type == InputTypeRelease) { - chip8->releaseCallback(InputTypeShort, chip8->context); - } - - return consumed; -} - -Chip8View* chip8_alloc() { - Chip8View* chip8 = malloc(sizeof(Chip8View)); - - chip8->view = view_alloc(); - view_allocate_model(chip8->view, ViewModelTypeLocking, sizeof(Chip8Model)); - view_set_context(chip8->view, chip8); - view_set_draw_callback(chip8->view, chip8_draw_callback); - view_set_input_callback(chip8->view, chip8_input_callback); - - return chip8; -} - -void chip8_free(Chip8View* chip8) { - furi_assert(chip8); - view_free(chip8->view); - free(chip8); -} - -View* chip8_get_view(Chip8View* chip8) { - furi_assert(chip8); - return chip8->view; -} - -void chip8_set_ok_callback(Chip8View* chip8, Chip8ViewCallback callback, void* context) { - furi_assert(chip8); - furi_assert(callback); - with_view_model( - chip8->view, (Chip8Model * model) { - chip8->callback = callback; - chip8->context = context; - return false; - }); -} - -void chip8_set_back_callback(Chip8View* chip8, Chip8ViewKeyBackCallback callback, void* context) { - furi_assert(chip8); - furi_assert(callback); - with_view_model( - chip8->view, (Chip8Model * model) { - chip8->backCallback = callback; - chip8->context = context; - return true; - }); -} - -void chip8_set_up_callback(Chip8View* chip8, Chip8ViewKeyUpCallback callback, void* context) { - furi_assert(chip8); - furi_assert(callback); - with_view_model( - chip8->view, (Chip8Model * model) { - chip8->upCallback = callback; - chip8->context = context; - return true; - }); -} - -void chip8_set_down_callback(Chip8View* chip8, Chip8ViewKeyDownCallback callback, void* context) { - furi_assert(chip8); - furi_assert(callback); - with_view_model( - chip8->view, (Chip8Model * model) { - chip8->downCallback = callback; - chip8->context = context; - return true; - }); -} - -void chip8_set_file_name(Chip8View* chip8, FuriString* name) { - furi_assert(name); - with_view_model( - chip8->view, (Chip8Model * model) { - *model->file_name = *name; - return false; - }); -} - -void chip8_set_backup_screen(Chip8View* chip8, uint8_t** screen) { - furi_assert(screen); - with_view_model( - chip8->view, (Chip8Model * model) { - model->backup_screen = screen; - return false; - }); -} - -void chip8_set_state(Chip8View* chip8, Chip8State* st) { - furi_assert(st); - with_view_model( - chip8->view, (Chip8Model * model) { - memcpy(&(model->state), st, sizeof(Chip8State)); - return true; - }); -} - -void chip8_set_release_callback(Chip8View* chip8, Chip8ViewReleaseCallback callback, void* context) { - furi_assert(chip8); - furi_assert(callback); - with_view_model( - chip8->view, (Chip8Model * model) { - chip8->releaseCallback = callback; - chip8->context = context; - return true; - }); -} diff --git a/applications/plugins/chip8/views/chip8_view.h b/applications/plugins/chip8/views/chip8_view.h deleted file mode 100644 index 7550c0326..000000000 --- a/applications/plugins/chip8/views/chip8_view.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include "../chip8.h" - -typedef struct Chip8View Chip8View; -typedef void (*Chip8ViewCallback)(InputType type, void* context); -typedef void (*Chip8ViewKeyBackCallback)(Chip8View* view, InputType type, void* context); -typedef void (*Chip8ViewKeyUpCallback)(InputType type, void* context); -typedef void (*Chip8ViewKeyDownCallback)(InputType type, void* context); -typedef void (*Chip8ViewReleaseCallback)(InputType type, void* context); - -Chip8View* chip8_alloc(); - -void chip8_free(Chip8View* chip8); - -View* chip8_get_view(Chip8View* chip8); - -void chip8_set_ok_callback(Chip8View* chip8, Chip8ViewCallback callback, void* context); -void chip8_set_back_callback(Chip8View* chip8, Chip8ViewKeyBackCallback callback, void* context); -void chip8_set_up_callback(Chip8View* chip8, Chip8ViewKeyUpCallback callback, void* context); -void chip8_set_down_callback(Chip8View* chip8, Chip8ViewKeyDownCallback callback, void* context); -void chip8_set_release_callback(Chip8View* chip8, Chip8ViewReleaseCallback callback, void* context); - -void chip8_set_backup_screen(Chip8View* chip8, uint8_t** screen); - -void chip8_set_file_name(Chip8View* chip8, FuriString* name); - -void chip8_set_state(Chip8View* chip8, Chip8State* st);