diff options
author | Alexey Yakovenko <wakeroid@gmail.com> | 2009-12-18 23:54:30 +0100 |
---|---|---|
committer | Alexey Yakovenko <wakeroid@gmail.com> | 2009-12-18 23:54:30 +0100 |
commit | 8674104981f0fc91ce504d8702572b8cd8b1fc25 (patch) | |
tree | d1e9ced526c40f51b2db20c0d96e58a3e531f65e /plugins/vtx | |
parent | 01740cc3f9dcfc8e95aae03003757e2b085ef70f (diff) |
vxt plugin WIP
Diffstat (limited to 'plugins/vtx')
-rw-r--r-- | plugins/vtx/COPYING | 340 | ||||
-rw-r--r-- | plugins/vtx/Makefile.am | 6 | ||||
-rw-r--r-- | plugins/vtx/ay8912.c | 494 | ||||
-rw-r--r-- | plugins/vtx/ayemu.h | 84 | ||||
-rw-r--r-- | plugins/vtx/ayemu_8912.h | 156 | ||||
-rw-r--r-- | plugins/vtx/ayemu_vtxfile.h | 73 | ||||
-rw-r--r-- | plugins/vtx/lh5dec.c | 304 | ||||
-rw-r--r-- | plugins/vtx/vtx.c | 162 | ||||
-rw-r--r-- | plugins/vtx/vtxfile.c | 283 |
9 files changed, 1902 insertions, 0 deletions
diff --git a/plugins/vtx/COPYING b/plugins/vtx/COPYING new file mode 100644 index 00000000..d60c31a9 --- /dev/null +++ b/plugins/vtx/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE 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. + + 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 +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This 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 Library General +Public License instead of this License. diff --git a/plugins/vtx/Makefile.am b/plugins/vtx/Makefile.am new file mode 100644 index 00000000..abfa8c98 --- /dev/null +++ b/plugins/vtx/Makefile.am @@ -0,0 +1,6 @@ +vtxdir = $(libdir)/$(PACKAGE) +pkglib_LTLIBRARIES = vtx.la +vtx_la_SOURCES = vtx.c ay8912.c ayemu_8912.h ayemu.h ayemu_vtxfile.h lh5dec.c vtxfile.c +vtx_la_LDFLAGS = -module + +AM_CFLAGS = $(CFLAGS) -std=c99 diff --git a/plugins/vtx/ay8912.c b/plugins/vtx/ay8912.c new file mode 100644 index 00000000..bf79f95c --- /dev/null +++ b/plugins/vtx/ay8912.c @@ -0,0 +1,494 @@ +/* AY/YM emulator implementation. */ + +#include "ayemu.h" + +#define debuglog stderr; + +char *ayemu_err; + +static const char VERSION[] = "libayemu 0.9"; + +const int MAGIC1 = 0xcdef; /* for check ayemu_t structure inited */ + +enum { +/* Max amplitude value for stereo signal for avoiding for possible + folowwing SSRC for clipping */ + AYEMU_MAX_AMP = 24575, + AYEMU_DEFAULT_CHIP_FREQ = 1773400 +}; + +/* sound chip volume envelops (will calculated by gen_env()) */ +static int bEnvGenInit = 0; +static int Envelope [16][128]; + + +/* AY volume table (c) by V_Soft and Lion 17 */ +static int Lion17_AY_table [16] = + { 0, 513, 828, 1239, 1923, 3238, 4926, 9110, + 10344, 17876, 24682, 30442, 38844, 47270, 56402, 65535}; + +/* YM volume table (c) by V_Soft and Lion 17 */ +static int Lion17_YM_table [32] = + { 0, 0, 190, 286, 375, 470, 560, 664, + 866, 1130, 1515, 1803, 2253, 2848, 3351, 3862, + 4844, 6058, 7290, 8559, 10474, 12878, 15297, 17787, + 21500, 26172, 30866, 35676, 42664, 50986, 58842, 65535}; + +/* AY volume table (c) by Hacker KAY */ +static int KAY_AY_table [16] = + { 0, 836, 1212, 1773, 2619, 3875, 5397, 8823, + 10392, 16706, 23339, 29292, 36969, 46421, 55195, 65535}; + +/* YM volume table (c) by Hacker KAY */ +static int KAY_YM_table [32] = + { 0, 0, 248, 450, 670, 826, 1010, 1239, + 1552, 1919, 2314, 2626, 3131, 3778, 4407, 5031, + 5968, 7161, 8415, 9622, 11421, 13689, 15957, 18280, + 21759, 26148, 30523, 34879, 41434, 49404, 57492, 65535}; + +/* default equlaizer (layout) settings for AY and YM, 7 stereo types */ +static const int default_layout [2][7][6] = { + { + /* A_l, A_r, B_l, B_r, C_l, C_r */ + + /* for AY */ + {100, 100, 100, 100, 100, 100}, // _MONO + {100, 33, 70, 70, 33, 100}, // _ABC + {100, 33, 33, 100, 70, 70}, // _ACB + {70, 70, 100, 33, 33, 100}, // _BAC + {33, 100, 100, 33, 70, 70}, // _BCA + {70, 70, 33, 100, 100, 33}, // _CAB + {33, 100, 70, 70, 100, 33}}, // _CBA + { + /* for YM */ + {100, 100, 100, 100, 100, 100}, // _MONO + {100, 5, 70, 70, 5, 100}, // _ABC + {100, 5, 5, 100, 70, 70}, // _ACB + {70, 70, 100, 5, 5, 100}, // _BAC + {5, 100, 100, 5, 70, 70}, // _BCA + {70, 70, 5, 100, 100, 5}, // _CAB + {5, 100, 70, 70, 100, 5}} // _CBA +}; + + +static int check_magic(ayemu_ay_t *ay) +{ + if (ay->magic == MAGIC1) + return 1; + fprintf(stderr, "libayemu: passed pointer %p to uninitialized ayemu_ay_t structure\n", ay); + return 0; +} + + +/* make chip hardware envelop tables. + Will execute once before first use. */ +static void gen_env() +{ + int env; + int pos; + int hold; + int dir; + int vol; + + for (env = 0; env < 16; env++) { + hold = 0; + dir = (env & 4)? 1 : -1; + vol = (env & 4)? -1 : 32; + for (pos = 0; pos < 128; pos++) { + if (!hold) { + vol += dir; + if (vol < 0 || vol >= 32) { + if ( env & 8 ) { + if ( env & 2 ) dir = -dir; + vol = (dir > 0 )? 0:31; + if ( env & 1 ) { + hold = 1; + vol = ( dir > 0 )? 31:0; + } + } else { + vol = 0; + hold = 1; + } + } + } + Envelope[env][pos] = vol; + } + } + bEnvGenInit = 1; +} + + +/** + * \retval ayemu_init none. + * +*/ +void ayemu_init(ayemu_ay_t *ay) +{ + ay->default_chip_flag = 1; + ay->ChipFreq = AYEMU_DEFAULT_CHIP_FREQ; + ay->default_stereo_flag = 1; + ay->default_sound_format_flag = 1; + ay->dirty = 1; + ay->magic = MAGIC1; + + ayemu_reset(ay); +} + +/** Reset AY/YM chip. + * + * \arg \c ay - pointer to ayemu_ay_t structure. + * \return none. + */ +void ayemu_reset(ayemu_ay_t *ay) +{ + if (!check_magic(ay)) return; + + ay->cnt_a = ay->cnt_b = ay->cnt_c = ay->cnt_n = ay->cnt_e = 0; + ay->bit_a = ay->bit_b = ay->bit_c = ay->bit_n = 0; + ay->env_pos = ay->EnvNum = 0; + ay->Cur_Seed = 0xffff; +} + + +static void set_table_ay (ayemu_ay_t *ay, int tbl[16]) +{ + int n; + for (n = 0; n < 32; n++) + ay->table[n] = tbl[n/2]; + ay->type = AYEMU_AY; +} + +static void set_table_ym (ayemu_ay_t *ay, int tbl[32]) +{ + int n; + for (n = 0; n < 32; n++) + ay->table[n] = tbl[n]; + ay->type = AYEMU_YM; +} + + +/** Set chip type. */ +int ayemu_set_chip_type(ayemu_ay_t *ay, ayemu_chip_t type, int *custom_table) +{ +if (!check_magic(ay)) + return 0; + + if (!(type == AYEMU_AY_CUSTOM || type == AYEMU_YM_CUSTOM) && custom_table != NULL) { + ayemu_err = "For non-custom chip type 'custom_table' param must be NULL"; + return 0; + } + + switch(type) { + case AYEMU_AY: + case AYEMU_AY_LION17: + set_table_ay(ay, Lion17_AY_table); + break; + case AYEMU_YM: + case AYEMU_YM_LION17: + set_table_ym(ay, Lion17_YM_table); + break; + case AYEMU_AY_KAY: + set_table_ay(ay, KAY_AY_table); + break; + case AYEMU_YM_KAY: + set_table_ym(ay, KAY_YM_table); + break; + case AYEMU_AY_CUSTOM: + set_table_ay(ay, custom_table); + break; + case AYEMU_YM_CUSTOM: + set_table_ym(ay, custom_table); + break; + default: + ayemu_err = "Incorrect chip type"; + return 0; + } + + ay->default_chip_flag = 0; + ay->dirty = 1; + return 1; +} + + +/** Set chip frequency. */ +void ayemu_set_chip_freq(ayemu_ay_t *ay, int chipfreq) +{ + if (!check_magic(ay)) return; + + ay->ChipFreq = chipfreq; + ay->dirty = 1; +} + +/*! Set output sound format + * \arg \c ay - pointer to ayemu_t structure + * \arg \c freq - sound freq (44100 for example) + * \arg \c chans - number of channels (1-mono, 2-stereo) + * \arg \c bits - now supported only 16 and 8. + * \retval \b 1 on success, \b 0 if error occure + */ +int ayemu_set_sound_format (ayemu_ay_t *ay, int freq, int chans, int bits) +{ + if (!check_magic(ay)) + return 0; + + if (!(bits == 16 || bits == 8)) { + ayemu_err = "Incorrect bits value"; + return 0; + } + else if (!(chans == 1 || chans == 2)) { + ayemu_err = "Incorrect number of channels"; + return 0; + } + else if (freq < 50) { + ayemu_err = "Incorrect output sound freq"; + return 0; + } + else { + ay->sndfmt.freq = freq; + ay->sndfmt.channels = chans; + ay->sndfmt.bpc = bits; + } + + ay->default_sound_format_flag = 0; + ay->dirty = 1; + return 1; +} + + +/*! Set amplitude factor for each of channels (A,B anc C, tone and noise). + * Factor's value must be from (-100) to 100. + * \arg ay - pointer to ayemu_t structure + * \arg stereo_type - type of stereo + * \arg custom_eq - NULL or pointer to custom table of mixer layout. + * \retval 1 if OK, 0 if error occures. + */ +int ayemu_set_stereo(ayemu_ay_t *ay, ayemu_stereo_t stereo_type, int *custom_eq) +{ + int i; + int chip; + + if (!check_magic(ay)) + return 0; + + if (stereo_type != AYEMU_STEREO_CUSTOM && custom_eq != NULL) { + ayemu_err = "Stereo type not custom, 'custom_eq' parametr must be NULL"; + return 0; + } + + chip = (ay->type == AYEMU_AY)? 0 : 1; + + switch(stereo_type) { + case AYEMU_MONO: + case AYEMU_ABC: + case AYEMU_ACB: + case AYEMU_BAC: + case AYEMU_BCA: + case AYEMU_CAB: + case AYEMU_CBA: + for (i = 0 ; i < 6 ; i++) + ay->eq[i] = default_layout[chip][stereo_type][i]; + break; + case AYEMU_STEREO_CUSTOM: + for (i = 0 ; i < 6 ; i++) + ay->eq[i] = custom_eq[i]; + break; + default: + ayemu_err = "Incorrect stereo type"; + return 0; + } + + ay->default_stereo_flag = 0; + ay->dirty = 1; + return 1; +} + + +#define WARN_IF_REGISTER_GREAT_THAN(r,m) \ +if (*(regs + r) > m) \ + fprintf(stderr, "ayemu_set_regs: warning: possible bad register data- R%d > %d\n", r, m) + + +/** Assign values for AY registers. + * + * You must pass array of char [14] to this function + */ +void ayemu_set_regs(ayemu_ay_t *ay, ayemu_ay_reg_frame_t regs) +{ + if (!check_magic(ay)) return; + + WARN_IF_REGISTER_GREAT_THAN(1,15); + WARN_IF_REGISTER_GREAT_THAN(3,15); + WARN_IF_REGISTER_GREAT_THAN(5,15); + WARN_IF_REGISTER_GREAT_THAN(8,31); + WARN_IF_REGISTER_GREAT_THAN(9,31); + WARN_IF_REGISTER_GREAT_THAN(10,31); + + ay->regs.tone_a = regs[0] + ((regs[1]&0x0f) << 8); + ay->regs.tone_b = regs[2] + ((regs[3]&0x0f) << 8); + ay->regs.tone_c = regs[4] + ((regs[5]&0x0f) << 8); + + ay->regs.noise = regs[6] & 0x1f; + + ay->regs.R7_tone_a = ! (regs[7] & 0x01); + ay->regs.R7_tone_b = ! (regs[7] & 0x02); + ay->regs.R7_tone_c = ! (regs[7] & 0x04); + + ay->regs.R7_noise_a = ! (regs[7] & 0x08); + ay->regs.R7_noise_b = ! (regs[7] & 0x10); + ay->regs.R7_noise_c = ! (regs[7] & 0x20); + + ay->regs.vol_a = regs[8] & 0x0f; + ay->regs.vol_b = regs[9] & 0x0f; + ay->regs.vol_c = regs[10] & 0x0f; + ay->regs.env_a = regs[8] & 0x10; + ay->regs.env_b = regs[9] & 0x10; + ay->regs.env_c = regs[10] & 0x10; + ay->regs.env_freq = regs[11] + (regs[12] << 8); + + if (regs[13] != 0xff) { /* R13 = 255 means continue curent envelop */ + ay->regs.env_style = regs[13] & 0x0f; + ay->env_pos = ay->cnt_e = 0; + } +} + + +static void prepare_generation(ayemu_ay_t *ay) +{ + int vol, max_l, max_r; + + if (!ay->dirty) return; + + if (!bEnvGenInit) gen_env (); + + if (ay->default_chip_flag) ayemu_set_chip_type(ay, AYEMU_AY, NULL); + + if (ay->default_stereo_flag) ayemu_set_stereo(ay, AYEMU_ABC, NULL); + + if (ay->default_sound_format_flag) ayemu_set_sound_format(ay, 44100, 2, 16); + + ay->ChipTacts_per_outcount = ay->ChipFreq / ay->sndfmt.freq / 8; + + { /* GenVols */ + int n, m; + int vol; + for (n = 0; n < 32; n++) { + vol = ay->table[n]; + for (m=0; m < 6; m++) + ay->vols[m][n] = (int) (((double) vol * ay->eq[m]) / 100); + } + } + + /* динамическая настройка глобального коэффициента усиления + подразумевается, что в vols [x][31] лежит самая большая громкость + TODO: Сделать проверку на это ;-) + */ + max_l = ay->vols[0][31] + ay->vols[2][31] + ay->vols[3][31]; + max_r = ay->vols[1][31] + ay->vols[3][31] + ay->vols[5][31]; + vol = (max_l > max_r) ? max_l : max_r; // =157283 on all defaults + ay->Amp_Global = ay->ChipTacts_per_outcount *vol / AYEMU_MAX_AMP; + + ay->dirty = 0; +} + + +/*! Generate sound. + * Fill sound buffer with current register data + * Return value: pointer to next data in output sound buffer + * \retval \b 1 if OK, \b 0 if error occures. + */ +void *ayemu_gen_sound(ayemu_ay_t *ay, void *buff, size_t sound_bufsize) +{ + int mix_l, mix_r; + int tmpvol; + int m; + int snd_numcount; + unsigned char *sound_buf = buff; + + if (!check_magic(ay)) + return 0; + + prepare_generation(ay); + + snd_numcount = sound_bufsize / (ay->sndfmt.channels * (ay->sndfmt.bpc >> 3)); + while (snd_numcount-- > 0) { + mix_l = mix_r = 0; + + for (m = 0 ; m < ay->ChipTacts_per_outcount ; m++) { + if (++ay->cnt_a >= ay->regs.tone_a) { + ay->cnt_a = 0; + ay->bit_a = ! ay->bit_a; + } + if (++ay->cnt_b >= ay->regs.tone_b) { + ay->cnt_b = 0; + ay->bit_b = ! ay->bit_b; + } + if (++ay->cnt_c >= ay->regs.tone_c) { + ay->cnt_c = 0; + ay->bit_c = ! ay->bit_c; + } + + /* GenNoise (c) Hacker KAY & Sergey Bulba */ + if (++ay->cnt_n >= (ay->regs.noise * 2)) { + ay->cnt_n = 0; + ay->Cur_Seed = (ay->Cur_Seed * 2 + 1) ^ \ + (((ay->Cur_Seed >> 16) ^ (ay->Cur_Seed >> 13)) & 1); + ay->bit_n = ((ay->Cur_Seed >> 16) & 1); + } + + if (++ay->cnt_e >= ay->regs.env_freq) { + ay->cnt_e = 0; + if (++ay->env_pos > 127) + ay->env_pos = 64; + } + +#define ENVVOL Envelope [ay->regs.env_style][ay->env_pos] + + if ((ay->bit_a | !ay->regs.R7_tone_a) & (ay->bit_n | !ay->regs.R7_noise_a)) { + tmpvol = (ay->regs.env_a)? ENVVOL : ay->regs.vol_a * 2 + 1; + mix_l += ay->vols[0][tmpvol]; + mix_r += ay->vols[1][tmpvol]; + } + + if ((ay->bit_b | !ay->regs.R7_tone_b) & (ay->bit_n | !ay->regs.R7_noise_b)) { + tmpvol =(ay->regs.env_b)? ENVVOL : ay->regs.vol_b * 2 + 1; + mix_l += ay->vols[2][tmpvol]; + mix_r += ay->vols[3][tmpvol]; + } + + if ((ay->bit_c | !ay->regs.R7_tone_c) & (ay->bit_n | !ay->regs.R7_noise_c)) { + tmpvol = (ay->regs.env_c)? ENVVOL : ay->regs.vol_c * 2 + 1; + mix_l += ay->vols[4][tmpvol]; + mix_r += ay->vols[5][tmpvol]; + } + } /* end for (m=0; ...) */ + + mix_l /= ay->Amp_Global; + mix_r /= ay->Amp_Global; + + if (ay->sndfmt.bpc == 8) { + mix_l = (mix_l >> 8) | 128; /* 8 bit sound */ + mix_r = (mix_r >> 8) | 128; + *sound_buf++ = mix_l; + if (ay->sndfmt.channels != 1) + *sound_buf++ = mix_r; + } else { + *sound_buf++ = mix_l & 0x00FF; /* 16 bit sound */ + *sound_buf++ = (mix_l >> 8); + if (ay->sndfmt.channels != 1) { + *sound_buf++ = mix_r & 0x00FF; + *sound_buf++ = (mix_r >> 8); + } + } + } + return sound_buf; +} + +/** Free all data allocated by emulator + * + * For now it do nothing. + */ +void ayemu_free (ayemu_ay_t *ay) +{ + /* nothing to do here */ + return; +} diff --git a/plugins/vtx/ayemu.h b/plugins/vtx/ayemu.h new file mode 100644 index 00000000..637cea91 --- /dev/null +++ b/plugins/vtx/ayemu.h @@ -0,0 +1,84 @@ +/* + ayemu - AY/YM sound chip emulator and music file loader + Copyright (C) 2003-2004 Sashnov Alexander + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Alexander Sashnov + sashnov@ngs.ru +*/ + +/*! + * \mainpage libayemu library + * + * Homepage on sourceforge.net: http://sourceforge.net/projects/libayemu + * + * This library will help you to add AY/YM music in your own game, demos, etc. + * + * For example of how to use it see playvtx and xmms-vtx packages. + * + * Python wrapper and example comming soon. + * + */ + +#ifndef _AYEMU_H +#define _AYEMU_H + +#ifdef __cplusplus +# define BEGIN_C_DECLS extern "C" { +# define END_C_DECLS } +#else /* !__cplusplus */ +# define BEGIN_C_DECLS +# define END_C_DECLS +#endif /* __cplusplus */ + +/* Make sure the correct platform symbols are defined */ +#if !defined(WIN32) && defined(_WIN32) +#define WIN32 +#endif /* Windows */ + +/* Some compilers use a special export keyword */ +#ifndef DECLSPEC +# ifdef __BEOS__ +# if defined(__GNUC__) +# define DECLSPEC __declspec(dllexport) +# else +# define DECLSPEC __declspec(export) +# endif +# else +# ifdef WIN32 +# ifdef __BORLANDC__ +# ifdef BUILD_SDL +# define DECLSPEC +# else +# define DECLSPEC __declspec(dllimport) +# endif +# else +# define DECLSPEC __declspec(dllexport) +# endif +# else +# define DECLSPEC +# endif +# endif +#endif + +#define EXTERN extern DECLSPEC + + +/* include other library headers */ +#include "ayemu_8912.h" +#include "ayemu_vtxfile.h" + +#endif diff --git a/plugins/vtx/ayemu_8912.h b/plugins/vtx/ayemu_8912.h new file mode 100644 index 00000000..e8e50e5a --- /dev/null +++ b/plugins/vtx/ayemu_8912.h @@ -0,0 +1,156 @@ +/** + * AY/YM emulator include file + */ + +#ifndef _AYEMU_ay8912_h +#define _AYEMU_ay8912_h + +#include <stddef.h> + +BEGIN_C_DECLS + +// TODO: ayemu_ay_t - hide all private data, allocate it in init function. + +typedef unsigned char ayemu_ay_reg_frame_t[14]; + + + +/** Types of stereo. + The codes of stereo types used for generage sound. */ +typedef enum +{ + AYEMU_MONO = 0, + AYEMU_ABC, + AYEMU_ACB, + AYEMU_BAC, + AYEMU_BCA, + AYEMU_CAB, + AYEMU_CBA, + AYEMU_STEREO_CUSTOM = 255 +} ayemu_stereo_t; + +/** Sound chip type. + Constant for identify used chip for emulation */ +typedef enum { + AYEMU_AY, /**< default AY chip (lion17 for now) */ + AYEMU_YM, /**< default YM chip (lion17 for now) */ + AYEMU_AY_LION17, /**< emulate AY with Lion17 table */ + AYEMU_YM_LION17, /**< emulate YM with Lion17 table */ + AYEMU_AY_KAY, /**< emulate AY with HACKER KAY table */ + AYEMU_YM_KAY, /**< emulate YM with HACKER KAY table */ + AYEMU_AY_LOG, /**< emulate AY with logariphmic table */ + AYEMU_YM_LOG, /**< emulate YM with logariphmic table */ + AYEMU_AY_CUSTOM, /**< use AY with custom table. */ + AYEMU_YM_CUSTOM /**< use YM with custom table. */ +} ayemu_chip_t; + +/** parsed by #ayemu_set_regs() AY registers data \internal */ +typedef struct +{ + int tone_a; /**< R0, R1 */ + int tone_b; /**< R2, R3 */ + int tone_c; /**< R4, R5 */ + int noise; /**< R6 */ + int R7_tone_a; /**< R7 bit 0 */ + int R7_tone_b; /**< R7 bit 1 */ + int R7_tone_c; /**< R7 bit 2 */ + int R7_noise_a; /**< R7 bit 3 */ + int R7_noise_b; /**< R7 bit 4 */ + int R7_noise_c; /**< R7 bit 5 */ + int vol_a; /**< R8 bits 3-0 */ + int vol_b; /**< R9 bits 3-0 */ + int vol_c; /**< R10 bits 3-0 */ + int env_a; /**< R8 bit 4 */ + int env_b; /**< R9 bit 4 */ + int env_c; /**< R10 bit 4 */ + int env_freq; /**< R11, R12 */ + int env_style; /**< R13 */ +} +ayemu_regdata_t; + + +/** Output sound format \internal */ +typedef struct +{ + int freq; /**< sound freq */ + int channels; /**< channels (1-mono, 2-stereo) */ + int bpc; /**< bits (8 or 16) */ +} +ayemu_sndfmt_t; + +/** + * \defgroup libayemu Functions for emulate AY/YM chip + */ +/*@{*/ + +/** Data structure for sound chip emulation \internal + * + */ +typedef struct +{ + /* emulator settings */ + int table[32]; /**< table of volumes for chip */ + ayemu_chip_t type; /**< general chip type (\b AYEMU_AY or \b AYEMU_YM) */ + int ChipFreq; /**< chip emulator frequency */ + int eq[6]; /**< volumes for channels. + Array contains 6 elements: + A left, A right, B left, B right, C left and C right; + range -100...100 */ + ayemu_regdata_t regs; /**< parsed registers data */ + ayemu_sndfmt_t sndfmt; /**< output sound format */ + + /* flags */ + int magic; /**< structure initialized flag */ + int default_chip_flag; /**< =1 after init, resets in #ayemu_set_chip_type() */ + int default_stereo_flag; /**< =1 after init, resets in #ayemu_set_stereo() */ + int default_sound_format_flag; /**< =1 after init, resets in #ayemu_set_sound_format() */ + int dirty; /**< dirty flag. Sets if any emulator properties changed */ + + int bit_a; /**< state of channel A generator */ + int bit_b; /**< state of channel B generator */ + int bit_c; /**< state of channel C generator */ + int bit_n; /**< current generator state */ + int cnt_a; /**< back counter of A */ + int cnt_b; /**< back counter of B */ + int cnt_c; /**< back counter of C */ + int cnt_n; /**< back counter of noise generator */ + int cnt_e; /**< back counter of envelop generator */ + int ChipTacts_per_outcount; /**< chip's counts per one sound signal count */ + int Amp_Global; /**< scale factor for amplitude */ + int vols[6][32]; /**< stereo type (channel volumes) and chip table. + This cache calculated by #table and #eq */ + int EnvNum; /**< number of current envilopment (0...15) */ + int env_pos; /**< current position in envelop (0...127) */ + int Cur_Seed; /**< random numbers counter */ +} +ayemu_ay_t; + +EXTERN void +ayemu_init(ayemu_ay_t *ay); + +EXTERN void +ayemu_reset(ayemu_ay_t *ay); + +EXTERN int +ayemu_set_chip_type(ayemu_ay_t *ay, ayemu_chip_t chip, int *custom_table); + +EXTERN void +ayemu_set_chip_freq(ayemu_ay_t *ay, int chipfreq); + +EXTERN int +ayemu_set_stereo(ayemu_ay_t *ay, ayemu_stereo_t stereo, int *custom_eq); + +EXTERN int +ayemu_set_sound_format (ayemu_ay_t *ay, int freq, int chans, int bits); + +EXTERN void +ayemu_set_regs (ayemu_ay_t *ay, ayemu_ay_reg_frame_t regs); + +EXTERN void* +ayemu_gen_sound (ayemu_ay_t *ay, void *buf, size_t bufsize); + +/*@}*/ + +END_C_DECLS + +#endif diff --git a/plugins/vtx/ayemu_vtxfile.h b/plugins/vtx/ayemu_vtxfile.h new file mode 100644 index 00000000..3856b45d --- /dev/null +++ b/plugins/vtx/ayemu_vtxfile.h @@ -0,0 +1,73 @@ +#ifndef _AYEMU_vtxfile_h +#define _AYEMU_vtxfile_h + +#include <stdio.h> +#include <stdint.h> + +#include "ayemu_8912.h" + +BEGIN_C_DECLS + +/** + * \defgroup vtxfile Functions for extract data from vtx files + */ +/*@{*/ + +/** structure for VTX file format handler + * \internal + * It stores VTX file header and current state + * (open file pointer, extracted register data, etc). + */ +typedef struct +{ + ayemu_chip_t chiptype; /**< Type of sound chip */ + int stereo; /**< Type of stereo: ABC, BCA,... */ + int loop; /**< song loop */ + int chipFreq; /**< AY chip freq (1773400 for ZX) */ + int playerFreq; /**< 50 Hz for ZX, 60 Hz for yamaha */ + int year; /**< year song composed */ + char *title; /**< song title */ + char *author; /**< song author */ + char *from; /**< song from */ + char *tracker; /**< tracker */ + char *comment; /**< comment */ + int regdata_size; /**< size of unpacked data (need for unpack). */ + unsigned char *regdata; /**< unpacked song data */ + size_t frames; /**< number of AY data frames */ +} ayemu_vtx_t; + + +/*! Load only header (song info) from vtx file. + * Purpose: read tags in play list. + */ +EXTERN ayemu_vtx_t * ayemu_vtx_header(const char *buf, size_t size); + +/*! Load song (header and unpack data). + * + */ +EXTERN ayemu_vtx_t * ayemu_vtx_load(const char *buf, size_t size); + +EXTERN void ayemu_vtx_getframe(const ayemu_vtx_t *vtx, size_t frame_n, + ayemu_ay_reg_frame_t regs); + +/** Free all allocaded data and structure itself. + * You must call this for any ayemu_vtx_t pointer allocated by this lib + * by functions ayemu_vtx_header() and ayemu_vtx_load(). + */ +EXTERN void ayemu_vtx_free(ayemu_vtx_t *vtx); + +/*! Load song header from file. + * Helper (non-all platform) function + */ +EXTERN ayemu_vtx_t * ayemu_vtx_header_from_file(const char *filename); + +/*! Load song header and data from file. + * Helper (non-all platform) function + */ +EXTERN ayemu_vtx_t * ayemu_vtx_load_from_file(const char *filename); + +/*@}*/ + +END_C_DECLS + +#endif diff --git a/plugins/vtx/lh5dec.c b/plugins/vtx/lh5dec.c new file mode 100644 index 00000000..1b2456bb --- /dev/null +++ b/plugins/vtx/lh5dec.c @@ -0,0 +1,304 @@ +/* extractiong lh5 module + (c) Haruhiko Okumura + (m) Roman Scherbakov +*/ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> /* memmove */ +#include <limits.h> + +static unsigned short bitbuf; + +#define BITBUFSIZ (CHAR_BIT * sizeof bitbuf) + +#define DICBIT 13 /* 12(-lh4-) or 13(-lh5-) */ +#define DICSIZ (1L << DICBIT) +#define MATCHBIT 8 /* bits for MAXMATCH - THRESHOLD */ +#define MAXMATCH 256 /* formerly F (not more than unsigned char_MAX + 1) */ +#define THRESHOLD 3 /* choose optimal value */ +#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD) +#define CBIT 9 /* $\lfloor \log_2 NC \rfloor + 1$ */ +#define CODE_BIT 16 /* codeword length */ +#define NP (DICBIT + 1) +#define NT (CODE_BIT + 3) +#define PBIT 4 /* smallest integer such that (1U << PBIT) > NP */ +#define TBIT 5 /* smallest integer such that (1U << TBIT) > NT */ +#if NT > NP +#define NPT NT +#else +#define NPT NP +#endif + +static unsigned long origsize, compsize; +static const unsigned char *in_buf; +static unsigned char *out_buf; + +static unsigned short subbitbuf; +static int bitcount; + +static unsigned short left[2 * NC - 1], right[2 * NC - 1]; +static unsigned char c_len[NC], pt_len[NPT]; +static unsigned short blocksize; + +static unsigned short c_table[4096], pt_table[256]; + +static int j; /* remaining bytes to copy */ + + +static void error(char *msg) +{ + fprintf(stderr, "libayemu: lh5dec.c: %s\n", msg); + exit(EXIT_FAILURE); +} + +static void fillbuf(int n) /* Shift bitbuf n bits left, read n bits */ +{ + bitbuf <<= n; + while (n > bitcount) { + bitbuf |= subbitbuf << (n -= bitcount); + if (compsize != 0) { + compsize--; subbitbuf = *in_buf++; + } else subbitbuf = 0; + bitcount = CHAR_BIT; + } + bitbuf |= subbitbuf >> (bitcount -= n); +} + +static unsigned short getbits(int n) +{ + unsigned short x; + + x = bitbuf >> (BITBUFSIZ - n); fillbuf(n); + return x; +} + +// make table for decoding + +static void make_table(int nchar, unsigned char bitlen[], int tablebits, unsigned short table[]) +{ + unsigned short count[17], weight[17], start[18], *p; + unsigned short i, k, len, ch, jutbits, avail, nextcode, mask; + + for (i = 1; i <= 16; i++) count[i] = 0; + for (i = 0; i < nchar; i++) count[bitlen[i]]++; + + start[1] = 0; + for (i = 1; i <= 16; i++) + start[i + 1] = start[i] + (count[i] << (16 - i)); + if (start[17] != (unsigned short)(1U << 16)) error("Bad table"); + + jutbits = 16 - tablebits; + for (i = 1; i <= tablebits; i++) { + start[i] >>= jutbits; + weight[i] = 1U << (tablebits - i); + } + while (i <= 16) weight[i++] = 1U << (16 - i); + + i = start[tablebits + 1] >> jutbits; + if (i != (unsigned short)(1U << 16)) { + k = 1U << tablebits; + while (i != k) table[i++] = 0; + } + + avail = nchar; + mask = 1U << (15 - tablebits); + for (ch = 0; ch < nchar; ch++) { + if ((len = bitlen[ch]) == 0) continue; + nextcode = start[len] + weight[len]; + if (len <= tablebits) { + for (i = start[len]; i < nextcode; i++) table[i] = ch; + } else { + k = start[len]; + p = &table[k >> jutbits]; + i = len - tablebits; + while (i != 0) { + if (*p == 0) { + right[avail] = left[avail] = 0; + *p = avail++; + } + if (k & mask) p = &right[*p]; + else p = &left[*p]; + k <<= 1; i--; + } + *p = ch; + } + start[len] = nextcode; + } +} + +// static Huffman + +static void read_pt_len(int nn, int nbit, int i_special) +{ + int i, c, n; + unsigned short mask; + + n = getbits(nbit); + if (n == 0) { + c = getbits(nbit); + for (i = 0; i < nn; i++) pt_len[i] = 0; + for (i = 0; i < 256; i++) pt_table[i] = c; + } else { + i = 0; + while (i < n) { + c = bitbuf >> (BITBUFSIZ - 3); + if (c == 7) { + mask = 1U << (BITBUFSIZ - 1 - 3); + while (mask & bitbuf) { mask >>= 1; c++; } + } + fillbuf((c < 7) ? 3 : c - 3); + pt_len[i++] = c; + if (i == i_special) { + c = getbits(2); + while (--c >= 0) pt_len[i++] = 0; + } + } + while (i < nn) pt_len[i++] = 0; + make_table(nn, pt_len, 8, pt_table); + } +} + +static void read_c_len(void) +{ + int i, c, n; + unsigned short mask; + + n = getbits(CBIT); + if (n == 0) { + c = getbits(CBIT); + for (i = 0; i < NC; i++) c_len[i] = 0; + for (i = 0; i < 4096; i++) c_table[i] = c; + } else { + i = 0; + while (i < n) { + c = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if (c >= NT) { + mask = 1U << (BITBUFSIZ - 1 - 8); + do { + if (bitbuf & mask) c = right[c]; + else c = left [c]; + mask >>= 1; + } while (c >= NT); + } + fillbuf(pt_len[c]); + if (c <= 2) { + if (c == 0) c = 1; + else if (c == 1) c = getbits(4) + 3; + else c = getbits(CBIT) + 20; + while (--c >= 0) c_len[i++] = 0; + } else c_len[i++] = c - 2; + } + while (i < NC) c_len[i++] = 0; + make_table(NC, c_len, 12, c_table); + } +} + + +static unsigned short decode_c(void) +{ + unsigned short j, mask; + + if (blocksize == 0) { + blocksize = getbits(16); + read_pt_len(NT, TBIT, 3); + read_c_len(); + read_pt_len(NP, PBIT, -1); + } + blocksize--; + j = c_table[bitbuf >> (BITBUFSIZ - 12)]; + if (j >= NC) { + mask = 1U << (BITBUFSIZ - 1 - 12); + do { + if (bitbuf & mask) j = right[j]; + else j = left [j]; + mask >>= 1; + } while (j >= NC); + } + fillbuf(c_len[j]); + return j; +} + + +static unsigned short decode_p(void) +{ + unsigned short j, mask; + + j = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if (j >= NP) { + mask = 1U << (BITBUFSIZ - 1 - 8); + do { + if (bitbuf & mask) j = right[j]; + else j = left [j]; + mask >>= 1; + } while (j >= NP); + } + fillbuf(pt_len[j]); + if (j != 0) j = (1U << (j - 1)) + getbits(j - 1); + return j; +} + + +static void decode(unsigned short count, unsigned char buffer[]) +{ + static unsigned short i; + unsigned short r, c; + + r = 0; + while (--j >= 0) { + buffer[r] = buffer[i]; + i = (i + 1) & (DICSIZ - 1); + if (++r == count) return; + } + for ( ; ; ) { + c = decode_c(); + if (c <= UCHAR_MAX) { + buffer[r] = c & UCHAR_MAX; + if (++r == count) return; + } else { + j = c - (UCHAR_MAX + 1 - THRESHOLD); + i = (r - decode_p() - 1) & (DICSIZ - 1); + while (--j >= 0) { + buffer[r] = buffer[i]; + i = (i + 1) & (DICSIZ - 1); + if (++r == count) return; + } + } + } +} + +void lh5_decode(const unsigned char *inp, unsigned char *outp, unsigned long original_size, unsigned long packed_size) +{ + unsigned short n; + unsigned char *buffer; + + compsize = packed_size; + origsize = original_size; + in_buf = inp; + out_buf = outp; + +#if 0 + fprintf(stderr, "DEBUG: compsize = %ld, origsize = %ld, first 8 bytes of packed data:\n", packed_size, original_size); + fprintf(stderr, " %02x %02x %02x %02x %02x %02x %02x %02x \n", + *(inp), *(inp+1),*(inp+2),*(inp+3), + *(inp+4),*(inp+5),*(inp+6),*(inp+7)); +#endif + + buffer = (unsigned char *) malloc(DICSIZ); + if (!buffer) error ("Out of memory"); + + bitbuf = 0; subbitbuf = 0; bitcount = 0; + fillbuf(BITBUFSIZ); + blocksize = 0; + j = 0; + + while (origsize != 0) { + n = (origsize > DICSIZ) ? DICSIZ : (unsigned short)origsize; + decode(n, buffer); + memmove(out_buf, buffer, n); + out_buf += n; + origsize -= n; + } + + if (buffer) free (buffer); + buffer = NULL; +} diff --git a/plugins/vtx/vtx.c b/plugins/vtx/vtx.c new file mode 100644 index 00000000..2014aee8 --- /dev/null +++ b/plugins/vtx/vtx.c @@ -0,0 +1,162 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009 Alexey Yakovenko + + 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 2 + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <stdlib.h> +#include <string.h> +#include "../../deadbeef.h" +#include "ayemu.h" + +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + +#define trace(...) { fprintf (stderr, __VA_ARGS__); } +//#define trace(fmt,...) + +static DB_decoder_t plugin; +static DB_functions_t *deadbeef; + +static const char * exts[] = { "vtx", NULL }; +static const char *filetypes[] = { "VTX", NULL }; + +static int +vtx_init (DB_playItem_t *it) { + // prepare to decode the track + // return -1 on failure + return 0; +} + +static void +vtx_free (void) { + // free everything allocated in _init +} + +static int +vtx_read_int16 (char *bytes, int size) { + // try decode `size' bytes + // return number of decoded bytes + // return 0 on EOF + return 0; +} + +static int +vtx_seek_sample (int sample) { + // seek to specified sample (frame) + // return 0 on success + // return -1 on failure +} + +static int +vtx_seek (float time) { + // seek to specified time in seconds + // return 0 on success + // return -1 on failure + // e.g. return vtx_seek_sample (time * samplerate); + return 0; +} + +static DB_playItem_t * +vtx_insert (DB_playItem_t *after, const char *fname) { + // read information from the track + // load/process cuesheet if exists + // insert track into playlist + // return track pointer on success + // return NULL on failure + + trace ("vtx_insert %s\n"); + // load header + DB_FILE *fp = deadbeef->fopen (fname); + if (!fp) { + trace ("vtx: failed to open file\n"); + return NULL; + } + char buf[0x4000]; + size_t sz; + sz = deadbeef->fread (buf, 1, sizeof (buf), fp); + deadbeef->fclose (fp); + if (sz <= 0) { + trace ("vtx: failed to read header data from file\n"); + return NULL; + } + ayemu_vtx_t *hdr = ayemu_vtx_header (buf, sz); + if (!hdr) { + trace ("vtx: failed to read header\n"); + return NULL; + } + + DB_playItem_t *it = deadbeef->pl_item_alloc (); + + it->decoder = &plugin; + it->fname = strdup (fname); + it->filetype = filetypes[0]; + + int totalsamples = 44100*60*5; + deadbeef->pl_set_item_duration (it, (float)totalsamples/hdr->playerFreq); + + // add metadata + deadbeef->pl_add_meta (it, "title", NULL); + + ayemu_vtx_free (hdr); + after = deadbeef->pl_insert_item (after, it); + return after; +} + +static int +vtx_start (void) { + // do one-time plugin initialization here + // e.g. starting threads for background processing, subscribing to events, etc + // return 0 on success + // return -1 on failure + return 0; +} +static int +vtx_stop (void) { + // undo everything done in _start here + // return 0 on success + // return -1 on failure + return 0; +} +// define plugin interface +static DB_decoder_t plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.version_major = 0, + .plugin.version_minor = 1, + .plugin.type = DB_PLUGIN_DECODER, + .plugin.name = "VTX decoder", + .plugin.descr = "AY8910/12 chip emulator and vtx file player", + .plugin.author = "Alexey Yakovenko", + .plugin.email = "waker@users.sourceforge.net", + .plugin.website = "http://deadbeef.sf.net", + .plugin.start = vtx_start, + .plugin.stop = vtx_stop, + .init = vtx_init, + .free = vtx_free, + .read_int16 = vtx_read_int16, +// .read_float32 = vtx_read_float32, + .seek = vtx_seek, + .seek_sample = vtx_seek_sample, + .insert = vtx_insert, + .exts = exts, + .id = "vtx", + .filetypes = filetypes +}; + +DB_plugin_t * +vtx_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} diff --git a/plugins/vtx/vtxfile.c b/plugins/vtx/vtxfile.c new file mode 100644 index 00000000..9dbeb9f7 --- /dev/null +++ b/plugins/vtx/vtxfile.c @@ -0,0 +1,283 @@ +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <ctype.h> +#include <unistd.h> + +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "ayemu.h" + +#define AYEMU_VTX_STRING_MAX 254 + +typedef const char * data_ptr_t; + + +/* LHA5 decoder, defined in lh5dec.c */ +extern void lh5_decode(unsigned const char *inp, + unsigned char *outp, + unsigned long original_size, + unsigned long packed_size); + +/* Read 8-bit integer from file. + */ +static data_ptr_t read_byte(data_ptr_t pos, int *p) +{ + const unsigned char *data = (const unsigned char*)pos; + *p = *data++; + return (data_ptr_t)data; +} + +/* Read 16-bit little-endian 1234 integer from file. + */ +static data_ptr_t read_word16(data_ptr_t pos, int *p) +{ + const unsigned char *data = (const unsigned char*)pos; + *p = *data++; + *p += *data++ << 8; + return (data_ptr_t)data; +} + +/* read 32-bit integer from file. + */ +static data_ptr_t read_word32(data_ptr_t pos, int *p) +{ + const unsigned char *data = (const unsigned char*)pos; + *p = *data++; + *p += *data++ << 8; + *p += *data++ << 16; + *p += *data++ << 24; + return (data_ptr_t)data; +} + +/* read_string: max 254 chars len (+1 for null terminator). + */ +static data_ptr_t read_string(data_ptr_t pos, char **res) +{ + int len; + + if (pos == NULL) + return NULL; + + len = strlen(pos); + + if (len > AYEMU_VTX_STRING_MAX) { + fprintf(stderr, "Error: string len more than %d (=%d)\n", AYEMU_VTX_STRING_MAX, len); + return NULL; + } + + *res = calloc(1, len + 1); + + strcpy(*res, pos); + + return pos + len + 1; +} + +static data_ptr_t read_header(data_ptr_t buf, ayemu_vtx_t **target, size_t size) +{ + char hdr[3]; + ayemu_vtx_t *vtx; + + hdr[0] = tolower(*buf++); + hdr[1] = tolower(*buf++); + hdr[2] = '\0'; + + if (size < 20) { + fprintf(stderr, "ayemu_vtx_open: file size < 20 bytes - it is impossible\n"); + return NULL; + } + + vtx = (ayemu_vtx_t*) calloc(1, sizeof(ayemu_vtx_t)); + + if (strncmp(hdr, "ay", 2) == 0) + vtx->chiptype = AYEMU_AY; + else if (strncmp (hdr, "ym", 2) == 0) + vtx->chiptype = AYEMU_YM; + else { + fprintf (stderr, "File is _not_ VORTEX format!\n" + "It not begins with 'ay' or 'ym' string.\n"); + goto clean_and_ret_null; + } + + buf = read_byte(buf, &vtx->stereo); + buf = read_word16(buf, &vtx->loop); + buf = read_word32(buf, &vtx->chipFreq); + buf = read_byte(buf, &vtx->playerFreq); + buf = read_word16(buf, &vtx->year); + buf = read_word32(buf, &vtx->regdata_size); + + buf = read_string(buf, &vtx->title); + buf = read_string(buf, &vtx->author); + buf = read_string(buf, &vtx->from); + buf = read_string(buf, &vtx->tracker); + buf = read_string(buf, &vtx->comment); + + *target = vtx; + return buf; + + clean_and_ret_null: + + ayemu_vtx_free(vtx); + return NULL; +} + + +ayemu_vtx_t * ayemu_vtx_header(data_ptr_t buf, size_t size) +{ + ayemu_vtx_t *vtx; + + const char *data; + + data = read_header(buf, &vtx, size); + + return vtx; +} + + +ayemu_vtx_t * ayemu_vtx_load(data_ptr_t buf, size_t size) +{ + ayemu_vtx_t *vtx; + + const char *data = read_header(buf, &vtx, size); + + if (! data) { + fprintf(stderr, "ayemu_vtx_load: Cannot parse file header\n"); + return NULL; + } + + // unpack data + size -= (buf - data); + + if ((vtx->regdata = (unsigned char *) malloc (vtx->regdata_size)) == NULL) { + fprintf (stderr, "ayemu_vtx_load_data: Can allocate %d bytes" + " for unpack register data\n", vtx->regdata_size); + return NULL; + } + + lh5_decode ((unsigned char*)data, vtx->regdata, vtx->regdata_size, size); + + vtx->frames = vtx->regdata_size / 14; + + return vtx; +} + + +/** Get specified data frame by number. + * + */ +void ayemu_vtx_getframe(const ayemu_vtx_t *vtx, size_t frame_n, ayemu_ay_reg_frame_t regs) +{ + int n; + + if (frame_n >= vtx->frames) + return; + + // calculate begin of data + unsigned char *p = vtx->regdata + frame_n; + + // step is data size / 14 + for(n = 0 ; n < 14 ; n++) { + regs[n] = *p; + p += vtx->frames; + } +} + +/** Free all allocaded resources. + * + */ +void ayemu_vtx_free(ayemu_vtx_t *vtx) +{ +#define FREE_PTR(x) if (x) { free(x); x = NULL; } + + FREE_PTR(vtx->title); + FREE_PTR(vtx->author); + FREE_PTR(vtx->from); + FREE_PTR(vtx->tracker); + FREE_PTR(vtx->comment); + FREE_PTR(vtx->regdata); +} + + + + +ayemu_vtx_t * ayemu_vtx_header_from_file(const char *filename) +{ + ayemu_vtx_t *ret; + size_t size; + const size_t page_size = (size_t) sysconf (_SC_PAGESIZE); + int fd; + struct stat st; + + // printf("Page size is %d\n", page_size); + + if (stat(filename, &st) != 0) { + fprintf(stderr, "Can't stat file %s: %s\n", filename, strerror(errno)); + return NULL; + } + size = st.st_size; + + fd = open(filename, O_RDONLY, 0); + if (fd == 0) { + fprintf(stderr, "Can't open file %s: %s\n", filename, strerror(errno)); + return NULL; + } + + size_t data_len = (size / page_size + 1) * page_size; + + char *data = mmap(NULL, data_len, PROT_READ, MAP_PRIVATE, fd, 0); + if (data == (void*)(-1)) { + fprintf(stderr, "Can't mmap file %s: %s\n", filename, strerror(errno)); + return NULL; + } + + ret = ayemu_vtx_header(data, size); + + if (munmap(data, data_len) != 0) { + fprintf(stderr, "Can't munmmap file %s: %s\n", filename, strerror(errno)); + } + + return ret; +} + + +ayemu_vtx_t * ayemu_vtx_load_from_file(const char *filename) +{ + size_t size; + const size_t page_size = (size_t) sysconf (_SC_PAGESIZE); + int fd; + struct stat st; + ayemu_vtx_t *ret; + + // printf("Page size is %d\n", page_size); + + if (stat(filename, &st) != 0) { + fprintf(stderr, "Can't stat file %s: %s\n", filename, strerror(errno)); + return NULL; + } + size = st.st_size; + + fd = open(filename, O_RDONLY, 0); + if (fd == 0) { + fprintf(stderr, "Can't open file %s: %s\n", filename, strerror(errno)); + return NULL; + } + + size_t data_len = (size / page_size + 1) * page_size; + + char *data = mmap(NULL, data_len, PROT_READ, MAP_PRIVATE, fd, 0); + if (data == (void*)(-1)) { + fprintf(stderr, "Can't mmap file %s: %s\n", filename, strerror(errno)); + return NULL; + } + + ret = ayemu_vtx_load(data, size); + + if (munmap(data, data_len) != 0) { + fprintf(stderr, "Can't munmmap file %s: %s\n", filename, strerror(errno)); + } + + return ret; +} |