├── mk.bat ├── example ├── drum.ftm └── drums.pently ├── .gitattributes ├── .gitignore ├── README.md ├── LICENSE └── ft2p.c /mk.bat: -------------------------------------------------------------------------------- 1 | gcc ft2p.c -o ft2p -Wall -std=c99 2 | -------------------------------------------------------------------------------- /example/drum.ftm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NovaSquirrel/ft2pently/HEAD/example/drum.ftm -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /example/drums.pently: -------------------------------------------------------------------------------- 1 | sfx kick on noise 2 | volume 12 10 8 6 4 3 2 1 1 3 | pitch 10 0 4 | 5 | sfx snare on noise 6 | volume 12 10 8 6 4 3 2 1 1 7 | pitch 4 10 8 | 9 | sfx hihat on noise 10 | volume 4 2 2 1 11 | pitch 12 12 | timbre | 0 1 13 | 14 | sfx openhat on noise 15 | volume 6 5 4 4 3 3 3 2 2 2 1 1 1 1 1 16 | pitch 12 17 | timbre | 0 1 18 | 19 | sfx snarehat on noise 20 | volume 6 5 4 4 3 3 3 2 2 2 1 1 1 1 1 21 | pitch 4 10 10 12 22 | timbre 0 0 | 0 1 23 | 24 | sfx trikick on triangle 25 | volume 15 15 15 2 2 26 | pitch e' c' a f# e 27 | 28 | sfx trisnare on triangle 29 | volume 15 15 2 2 30 | pitch bb' ab' g' f#' 31 | 32 | drum kick kick 33 | drum snare snare 34 | drum clhat hihat 35 | drum ohat openhat 36 | drum tkick kick trikick 37 | drum tsnare snare trisnare 38 | drum tsohat snarehat trisnare 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # test stuff 2 | test/ 3 | 4 | # Pently stuff 5 | *.txt 6 | *.pently 7 | 8 | # other stuff 9 | *.ftm 10 | test.bat 11 | 12 | # Object files 13 | *.o 14 | *.ko 15 | *.obj 16 | *.elf 17 | 18 | # Precompiled Headers 19 | *.gch 20 | *.pch 21 | 22 | # Libraries 23 | *.lib 24 | *.a 25 | *.la 26 | *.lo 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | # Debug files 43 | *.dSYM/ 44 | 45 | # ========================= 46 | # Operating System Files 47 | # ========================= 48 | 49 | # OSX 50 | # ========================= 51 | 52 | .DS_Store 53 | .AppleDouble 54 | .LSOverride 55 | 56 | # Thumbnails 57 | ._* 58 | 59 | # Files that might appear in the root of a volume 60 | .DocumentRevisions-V100 61 | .fseventsd 62 | .Spotlight-V100 63 | .TemporaryItems 64 | .Trashes 65 | .VolumeIcon.icns 66 | 67 | # Directories potentially created on remote AFP share 68 | .AppleDB 69 | .AppleDesktop 70 | Network Trash Folder 71 | Temporary Items 72 | .apdisk 73 | 74 | # Windows 75 | # ========================= 76 | 77 | # Windows image file caches 78 | Thumbs.db 79 | ehthumbs.db 80 | 81 | # Folder config file 82 | Desktop.ini 83 | 84 | # Recycle Bin used on file shares 85 | $RECYCLE.BIN/ 86 | 87 | # Windows Installer files 88 | *.cab 89 | *.msi 90 | *.msm 91 | *.msp 92 | 93 | # Windows shortcuts 94 | *.lnk 95 | *.zip 96 | 97 | # Unignore the example files 98 | !example/*.ftm 99 | !example/*.pently 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ft2pently 2 | ========= 3 | Famitracker to Pently music converter 4 | 5 | ft2pently takes Famitracker's text exports and converts it to the Pently music engine's MML-like format. 6 | 7 | Windows binaries are available [on the release page](https://github.com/NovaSquirrel/ft2pently/releases). 8 | 9 | Limitations 10 | ----------------------- 11 | As with the other music engines that offer conversions from Famitracker, composers have to limit the Famitracker effects they use, though Pently is much less limiting than say, Famitone2. 12 | Most effects are unsupported, as are the "pitch" and "hi-pitch" envelopes. 13 | If using 0CC Famitracker, you can get a better approximation of how vibrato sounds in Pently by setting it to linear pitch. 14 | 15 | See also [https://wiki.nesdev.com/w/index.php/Audio_drivers#Pently this list]. 16 | 17 | Supported effects: 18 | * 0xy - Arpeggio 19 | * Bxx - Set loop point (loop to frame xx) 20 | * Cxx - Stop song 21 | * Dxx - Pattern cut (xx ignored, always zero) 22 | * Fxx - Tempo/speed change 23 | * Gxx - Delay note start 24 | * Sxx - Delay note cut 25 | * 300 - Disable slur 26 | * 3xx - Enable slur (if x is nonzero) 27 | * 4xy - Vibrato of depth Y. X (speed) is ignored; use 5 to approximate Pently's vibrato speed in Famitracker. Valid depths are 0 through 4, where 4 is very strong and 0 is disabled. 28 | * Qxy - Play note for one row then slur up Y semitones 29 | * Rxy - Play note for one row then slur down Y semitones 30 | 31 | Things to keep in mind: 32 | * Always define a duty envelope for square instruments, even if it's 12.5%. In Pently, an unspecified duty cycle is 50%, so a duty envelope needs to be defined. 33 | * An instrument's envelope will only last as long as the volume envelope. If your instrument's arpeggio or duty envelopes are longer than the volume envelope and you want the whole arpeggio/duty envelope to play, extend the volume envelope to match. 34 | * Triangle channel volume is still used for determining whether a note should be interrupted by a sound effect or not. 35 | * Triangle instrument duty must be 50% (or unspecified, which defaults to 50%) or the note will cut prematurely. 36 | * The volume column is supported, but can only do 25%, 50%, 75% and 100% volume rather than the range Famitracker has. 0-6 maps to 25%, 6-9 maps to 50%, A-C maps to 75% and D-F maps to 100%. 37 | 38 | Attack channel 39 | -------------- 40 | 41 | Pently supports sharing one channel between two different patterns. To use it in ft2pently, set the module to MMC5 and puts notes on the first square channel. To specify what channel is being interrupted by the attack, put the Jxx effect in the track, with 0 for the first square channel, 1 for the second square channel, or 2 for the triangle channel. 42 | 43 | The channel being interrupted must be at the end of its volume envelope by the time the attack happens, because it won't resume the earlier attack. 44 | 45 | An instrument WILL return to a decay if it was interrupted during a decay, however. See the "Auto decay" section in this manual for information on how to use decay. 46 | 47 | Drums 48 | ----- 49 | 50 | Drums are probably the area where Pently differs from Famitracker's model the most, so ft2pently needs some help to convert them. Instead of a generic noise channel, Pently has a "drum" channel that is based around sound effects, allowing the drum patterns to be stored very efficiently. There is a hard limit on 25 drum types, so keep this in mind! 51 | 52 | Alongside the space savings, Pently's drum system allows for drums that use two channels at once, most commonly using noise and triangle together. This allows for very good sounding drums even without DPCM, and unlike the usual case with this strategy you don't need to mess with the triangle channel in Famitracker to "bake" drums into the triangle patterns. 53 | 54 | There are a few different choices for how you specify drums in your FTM file: 55 | * Auto noise: Insert `auto noise` in your FTM file's comments. Compose a noise channel track normally in Famitracker, and each pitch a noise instrument is played at becomes a new Pently drum automatically. Recommended if you just want to quickly get things to work. 56 | * Auto dual drums: Insert `auto dual drums` in your FTM file's comments. Use `fixed` arpeggio envelopes on your noise and triangle instruments you want to use for drums, create the noise track as normal and insert Jxx effects, where the xx selects a corresponding triangle instrument. Look at Nova the Squirrel's repository for an example of this style. 57 | * Assigning drums to specific DPCM channel pitches (see next section) 58 | 59 | ft2pently can handle drums three different ways. If your game's drums are just noise instruments and you're happy with them, just put `auto noise` in the .ftm's comments and you're done! 60 | 61 | Keep in mind that Pently has a hard limit of 25 drums (because drums map to notes, and Pently can only see about two octaves at a time), and each frequency a noise instrument gets used at counts as another drum, so be careful not to use too many. Also, keep in mind that if `auto noise` mode is on, the DPCM channel in the .ftm is ignored. The noise channel and DPCM channels don't mix together. 62 | 63 | Using native Pently drums 64 | ------------------------- 65 | 66 | Set up the DPCM instrument as you usually would, with samples assigned to different notes. The samples aren't actually used in the conversion, but will let you approximate what the drum section of the song will sound like as you're composing it. 67 | 68 | You need to make a file containing drum definitions for pentlyas. [The pentlyas manual](https://github.com/Qix-/pently/blob/master/docs/pentlyas.md) covers how to define drums. Drums that already sound nice can be found in [the sample songs](https://github.com/Qix-/pently/blob/master/src/musicseq.pently). 69 | 70 | Now that the sound of the drums are defined, ft2pently needs to know what DPCM channel notes correspond to which drums. This is done by putting commands for ft2pently in the song's comments section, reached with `Modules -> Comments` from the menu. (Make sure to have a blank line at the end of the comments) 71 | 72 | A sample drum configuration is as follows (with explanation): 73 | 74 | ``` 75 | include drums.pently 76 | drum c3 tkick 77 | drum c#3 tsnare 78 | drum d3 clhat 79 | ``` 80 | 81 | `include` reads another file and dumps it right into the output file along with the conversion, for sound effects and drums and such. Here it is including the drum definitions. 82 | 83 | `drum` specifies that a given DPCM channel note and octave corresponds to a given drum in Pently. Here, C, C# and D in octave 3 are used. 84 | 85 | Converting instruments to drums 86 | ------------------------------- 87 | 88 | Instead of importing drums, you can define drums using instruments. The DPCM channel must still be used as described in the previous section. As stated above, drums consist of one or two sound effects. For the arpeggio envelope, use the `fixed` type. 89 | 90 | ``` 91 | sfx 01 t tri_kick 92 | sfx 02 n noise_kick 93 | drumsfx tkick tri_kick noise_kick 94 | drum c3 tkick 95 | ``` 96 | 97 | `sfx` defines a new sound effect. It takes an instrument number (hexadecimal), a channel (s, t or n for square/pulse, triangle, or noise respectively), and a name to give the new sound effect (alphanumeric and underscores only). 98 | 99 | `drumsfx` defines a new drum, using one or two sound effects. It takes the drum name, and then the names of the sound effect(s) used. Same naming restrictions. 100 | 101 | `drum` works as before. 102 | 103 | Auto decay 104 | ---------- 105 | Pently can split an instrument into "attack" and "decay" sections, with the attack being a Famitracker-ish volume envelope and the decay being a linear slope down towards silence. This saves space, as a long fadeout at the end of an instrument does not need to be in the ROM. 106 | 107 | To use this feature, add a line containing "auto decay" to the .ftm's comments. 108 | Use my [decay envelope generator page](http://t.novasquirrel.com/test/decay.html) to create a decay envelope, and then paste the generated envelope onto the end of a volume envelope. 109 | A volume envelope may contain a decay and nothing else, if you don't want to use an attack. 110 | 111 | Important note: Auto decay will not activate for a given instrument if it would interfere with the duty and/or arpeggio envelopes. At the point in the volume envelope where the decay envelope starts, the duty and arpeggio envelopes must have already completed. This also means that those envelopes cannot be looped. 112 | 113 | Command line arguments 114 | ---------------------- 115 | By default, ft2pently will only warn about unsupported effects. To make it give an error and stop instead, add the `-strict` flag. 116 | 117 | Errors will display row numbers in decimal by default, but you can choose hex row numbers with `-hexrow`. 118 | 119 | Auto noise and auto decay can be turned on from the command line with `-autonoise` and `-autodecay` if you prefer that over comments. 120 | 121 | `-dotted` will make ft2pently use '.'s when writing durations. 122 | 123 | Converting the song 124 | ------------------- 125 | In Famitracker, either use `File -> Export text` from the menu, or `famitracker.exe song.ftm -export song.txt` from a terminal to make a text export of the song. 126 | 127 | Now, to run ft2pently: `ft2p -i song.txt -o song.pently` 128 | 129 | This output will need to be run through `pentlyas` to result in something the Pently engine can use. 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /ft2p.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ft2pently 3 | * 4 | * Copyright (C) 2016-2018 NovaSquirrel 5 | * 6 | * This program is free software: you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | // https://github.com/Qix-/pently/issues/4 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // maximum values, used for array sizes 28 | #define MAX_EFFECTS 4 29 | #define MAX_ROWS 256 30 | #define MAX_FRAMES 128 // real max is 128 31 | #define MAX_PATTERNS 128 // real max is 128 32 | #define MAX_INSTRUMENTS 64 // pently limit is 256/5 33 | #define MAX_MACRO_LEN 255 // real max is like 254? 34 | #define NUM_OCTAVES 7 35 | #define NUM_SEMITONES 12 36 | #define MAX_SFX 64 37 | #define MAX_DECAY_START 15 // starting volume 38 | #define MAX_DECAY_RATE 16 // decay rate 39 | #define MAX_DECAY_LEN 256 // actually goes up to 224 or something but this is to be safe 40 | #define MAX_SONGS 64 41 | #define SONG_NAME_LEN 32 42 | #define MAX_DRUMS 25 43 | 44 | //////////////////// constants //////////////////// 45 | const char *scale = "cCdDefFgGaAb"; 46 | const char *supported_effects = ".034BCDFGQRSJ"; 47 | const char *chan_name[] = {"pulse1", "pulse2", "triangle", "noise", "drum", "attack"}; 48 | const char *envelope_types[] = {"volume", "arpeggio", "pitch", "hipitch", "duty"}; 49 | 50 | //////////////////// enums and structs //////////////////// 51 | // sound channels 52 | enum { 53 | CH_SQUARE1, 54 | CH_SQUARE2, 55 | CH_TRIANGLE, 56 | CH_NOISE, 57 | CH_DPCM, // not supported, but used for Pently drums 58 | CH_ATTACK, // MMC5's first expansion square channel 59 | CHANNEL_COUNT 60 | }; 61 | 62 | // types of envelopes 63 | enum { 64 | MS_VOLUME, 65 | MS_ARPEGGIO, 66 | MS_PITCH, 67 | MS_HIPITCH, 68 | MS_DUTY, 69 | MACRO_SET_COUNT 70 | }; 71 | 72 | // arpeggio types 73 | enum { 74 | ARP_ABSOLUTE, 75 | ARP_RELATIVE, 76 | ARP_FIXED 77 | }; 78 | 79 | // supported note effects 80 | enum { 81 | FX_NONE = 0, 82 | FX_ARP = '0', // arpeggio 83 | FX_SLUR = '3', // enable slur if nonzero 84 | FX_VIBRATO = '4', // vibrato change 85 | FX_LOOP = 'B', // jump to frame X 86 | FX_FINE = 'C', // stop song 87 | FX_PAT_CUT = 'D', // skip to next pattern 88 | FX_TEMPO = 'F', // change tempo or speed 89 | FX_DELAY = 'G', // delay for X frames 90 | FX_SLUR_UP = 'Q', // note for one row, slur into pitch X semitones up 91 | FX_SLUR_DN = 'R', // note for one row, slur into pitch X semitones down 92 | FX_DELAYCUT = 'S', // grace note for X frames then rest 93 | FX_ATTACK_ON= 'J' // repurposed to specify attack target 94 | }; 95 | 96 | // volumes 97 | enum { 98 | VOL_SAME, // no change 99 | VOL_FF, // 100% 100 | VOL_MF, // 75% 101 | VOL_MP, // 50% 102 | VOL_PP, // 25% 103 | }; 104 | 105 | // a sound effect definition, held onto until end of export 106 | // used for drums and auto-generated noise drums 107 | typedef struct soundeffect { 108 | uint8_t instrument, channel; 109 | uint8_t pitch; // pitch offset for auto noise, or 0 110 | char name[64]; 111 | } soundeffect; 112 | 113 | // a note on a pattern 114 | typedef struct ftnote { 115 | uint8_t octave; // octave number 116 | char note; // note name, capitalized if sharp 117 | int8_t instrument; // instrument number 118 | uint8_t volume; // note volume, uses VOL_* values 119 | char effect[MAX_EFFECTS]; // effect letter 120 | uint8_t param[MAX_EFFECTS]; // effect parameter 121 | uint8_t slur; // nonzero if note has slur 122 | } ftnote; 123 | 124 | // a song and its patterns 125 | typedef struct ftsong { 126 | // Explicitly stated song information 127 | char real_name[SONG_NAME_LEN]; // name to display for errors 128 | char name[SONG_NAME_LEN]; // sanitized name for the actual file 129 | int rows, speed, tempo; 130 | 131 | // Buffers to hold song information 132 | int frame[MAX_FRAMES][CHANNEL_COUNT]; 133 | ftnote pattern[MAX_PATTERNS][CHANNEL_COUNT][MAX_ROWS]; 134 | uint8_t pattern_used[MAX_PATTERNS][CHANNEL_COUNT]; 135 | int pattern_length[MAX_PATTERNS][CHANNEL_COUNT]; 136 | int effect_columns[CHANNEL_COUNT]; // number of effect columns 137 | int loop_to; // frame to insert the segno at, or -1 for no looping 138 | 139 | // Song status information for parsing purposes 140 | int pattern_id, frames; 141 | } ftsong; 142 | 143 | // an instument envelope 144 | typedef struct ftmacro { 145 | int8_t length, loop, release; 146 | int8_t arp_type; // relative/absolute? not used by ft2pently 147 | int8_t sequence[MAX_MACRO_LEN]; 148 | uint8_t decay_rate; // if 0, decay isn't used 149 | uint8_t decay_volume; // starting volume to use for decay 150 | uint8_t decay_index; // index decay starts 151 | } ftmacro; 152 | 153 | //////////////////// functions //////////////////// 154 | 155 | // creates a note with some given information 156 | ftnote make_note(uint8_t octave, char note, int8_t instrument) { 157 | ftnote new_note; 158 | memset(&new_note, 0, sizeof(new_note)); 159 | new_note.octave = octave; 160 | new_note.note = note; 161 | new_note.instrument = instrument; 162 | return new_note; 163 | } 164 | 165 | // returns 1 for channels that have notes and a pitch 166 | static inline int channel_is_pitched(int channel) { 167 | return channel != CH_DPCM && channel != CH_NOISE; 168 | } 169 | 170 | // convert a note and an octave into a semitone number 171 | int note_to_semitone(char note, int octave) { 172 | return (strchr(scale, note)-scale)+(octave*NUM_SEMITONES); 173 | } 174 | 175 | // convert the number back to a note name and octave 176 | void semitone_to_note(int semitone, char *note, uint8_t *octave) { 177 | *note = scale[semitone % NUM_SEMITONES]; 178 | *octave = semitone / NUM_SEMITONES; 179 | } 180 | 181 | // offsets a note by a given number of semitones 182 | void shift_semitones(ftnote *note, int offset) { 183 | if(!isalpha(note->note)) 184 | return; 185 | // convert to semitones so I can just shift the integer value 186 | int semitones = note_to_semitone(note->note, note->octave); 187 | // add to the integer value 188 | semitones += offset; 189 | // change back to a note 190 | semitone_to_note(semitones, ¬e->note, ¬e->octave); 191 | } 192 | 193 | // asserts that a value is in a given range 194 | void check_range(const char *name, int value, int low, int high, const char *location) { 195 | if(value >= low && value < high) 196 | return; 197 | printf("Error: %s out of range (%i, must be below %i) %s\n", name, value, high, location?location:""); 198 | exit(-1); 199 | } 200 | 201 | // like strncpy but good 202 | void strlcpy(char *Destination, const char *Source, int MaxLength) { 203 | // MaxLength is directly from sizeof() so it includes the zero 204 | int SourceLen = strlen(Source); 205 | if((SourceLen+1) < MaxLength) 206 | MaxLength = SourceLen + 1; 207 | memcpy(Destination, Source, MaxLength-1); 208 | Destination[MaxLength-1] = 0; 209 | } 210 | 211 | // removes one line ending if found 212 | void remove_line_ending(char *text, char ending) { 213 | text = strrchr(text, ending); 214 | if(text) 215 | *text = 0; 216 | } 217 | 218 | // removes \n, \r or " if found on the end of a string 219 | void remove_line_endings(char *buffer) { 220 | remove_line_ending(buffer, '\n'); 221 | remove_line_ending(buffer, '\r'); 222 | remove_line_ending(buffer, '\"'); 223 | } 224 | 225 | // makes a label-friendly version of a name 226 | char *sanitize_name(char *outbuf, const char *input, int length) { 227 | char hex[3]; 228 | char temp[strlen(input)*2+1]; 229 | char *output = temp; 230 | 231 | if(!isalpha(*input) && *input != '_') // names usually have to start with an letter 232 | *(output++) = '_'; 233 | while(*input) { 234 | if(isalnum(*input)) // copy directly if alphanumeric 235 | *(output++) = *input; 236 | else if(*input == ' ' || *input == '-' || *input == '_') { // change certain characters to underscores 237 | *(output++) = '_'; 238 | } else { // escape other characters into their hexadecimal code 239 | sprintf(hex, "%.2x", *input); 240 | strcpy(output, hex); 241 | output += 2; 242 | } 243 | input++; 244 | } 245 | *(output) = 0; 246 | 247 | strlcpy(outbuf, temp, length); 248 | return outbuf; 249 | } 250 | 251 | // return 1 if a string starts with another specific string 252 | int starts_with(char *string, const char *start, char **arg) { 253 | // optionally, set a pointer to the spot in the string after the text being matched against 254 | if(arg) 255 | *arg = string+strlen(start); 256 | return !memcmp(string, start, strlen(start)); 257 | } 258 | 259 | // increases a pointer until it gets to a digit or a dash 260 | char *skip_to_number(char *string) { 261 | while(*string && !isdigit(*string) && *string!='-') 262 | string++; 263 | return string; 264 | } 265 | 266 | //////////////////// global variables //////////////////// 267 | ftsong song; // song being parsed 268 | ftsong xsong; // song being exported 269 | 270 | // module parsing state 271 | int song_num = 0, sfx_num = 0; 272 | int8_t instrument[MAX_INSTRUMENTS][MACRO_SET_COUNT]; 273 | uint8_t instrument_used[MAX_INSTRUMENTS]; 274 | uint8_t instrument_ignore[MAX_INSTRUMENTS]; 275 | int num_auto_drums = 0; 276 | uint8_t auto_drum_noise[MAX_DRUMS]; 277 | uint8_t auto_drum_tri[MAX_DRUMS]; 278 | ftmacro instrument_macro[MACRO_SET_COUNT][MAX_INSTRUMENTS]; 279 | char instrument_name[MAX_INSTRUMENTS][32]; 280 | uint16_t instrument_noise[MAX_INSTRUMENTS]; // each bit in each 16-bit value corresponds to a needed frequency 281 | char drum_name[NUM_OCTAVES][NUM_SEMITONES][16]; 282 | soundeffect soundeffects[MAX_SFX]; 283 | int duplicate_name_counter = 0; 284 | char song_name[MAX_SONGS][SONG_NAME_LEN]; // exists solely to check for duplicates 285 | 286 | // export options 287 | int decay_enabled = 0; // use the decay feature 288 | int auto_noise = 0; // automatically convert noise instruments to drums 289 | int auto_dual_drums = 0; // automatically convert fixed arpeggio noise instruments to drums (with triangle part) 290 | int hex_rows = 0; // display row numbers in hex instead of decimal 291 | int strict = 0; // turn warnings into errors 292 | int tri_sxx_to_cut = 0; // convert delayed triangle note cuts to regular note cuts 293 | int dotted_durations = 0; // use dotted durations in the output file 294 | char decay_envelope[MAX_DECAY_START][MAX_DECAY_RATE][MAX_DECAY_LEN]; // pre-calculated decay tables 295 | const char *in_filename, *out_filename; 296 | 297 | // displays a warning or an error 298 | void error(int stop, const char *fmt, ...) { 299 | if(strict) 300 | stop = 1; 301 | va_list args; 302 | va_start(args, fmt); 303 | printf((stop)?"Error: ":"Warning: "); 304 | vprintf(fmt, args); 305 | putchar('\n'); 306 | va_end(args); 307 | if(stop) 308 | exit(-1); 309 | } 310 | 311 | // creates a string that describes a location in a song 312 | const char *error_location(ftsong *the_song, int channel, int pattern, int row) { 313 | static char buffer[200]; 314 | 315 | if(hex_rows) { 316 | if(row == -1) 317 | sprintf(buffer, "[%s - %s pattern $%x]", the_song->real_name, chan_name[channel], pattern); 318 | else 319 | sprintf(buffer, "[%s - %s pattern $%x row $%x]", the_song->real_name, chan_name[channel], pattern, row); 320 | } else { 321 | if(row == -1) 322 | sprintf(buffer, "[%s - %s pattern %i]", the_song->real_name, chan_name[channel], pattern); 323 | else 324 | sprintf(buffer, "[%s - %s pattern %i row %i]", the_song->real_name, chan_name[channel], pattern, row); 325 | } 326 | return buffer; 327 | } 328 | 329 | // finds a auto/dual drum automatically, or creates a new one if necessary 330 | uint8_t find_auto_drum(uint8_t noise, uint8_t triangle) { 331 | for(int i=0; ilength; i++) { 349 | if(i == macro->loop) 350 | fprintf(file, "| "); 351 | fprintf(file, "%i ", macro->sequence[i]); 352 | } 353 | fprintf(file, "\n"); 354 | } 355 | 356 | // writes an octave using ' and , 357 | void write_octave(FILE *file, int octave) { 358 | int i; 359 | if(octave > 2) 360 | for(i=2; i!= octave; i++) 361 | fputc('\'', file); 362 | if(octave < 2) 363 | for(i=2; i!= octave; i--) 364 | fputc(',', file); 365 | } 366 | 367 | // flags for write_instrument 368 | enum { 369 | ABSOLUTE_PITCH = 1, 370 | ALLOW_DECAY = 2, 371 | }; 372 | 373 | // writes an instrument's envelopes 374 | void write_instrument(FILE *file, int i, int flags) { 375 | unsigned int num_macro_volume = (unsigned)instrument[i][MS_VOLUME]; 376 | unsigned int num_macro_duty = (unsigned)instrument[i][MS_DUTY]; 377 | unsigned int num_macro_arp = (unsigned)instrument[i][MS_ARPEGGIO]; 378 | 379 | // write the envelopes the instrument has 380 | if(instrument[i][MS_VOLUME] >= 0) { 381 | // read the decay information first to find out if the instrument has an automatic decay 382 | // (and make a copy of the macro that can be modified without changing the original) 383 | ftmacro macro = instrument_macro[MS_VOLUME][num_macro_volume]; 384 | int decay_rate = macro.decay_rate; 385 | int decay_volume = macro.decay_volume; 386 | int decay_index = macro.decay_index; 387 | 388 | // do not use decay if it would interfere with the arpeggio or duty envelopes, or if disallowed 389 | if((decay_rate && decay_enabled && (flags & ALLOW_DECAY)) 390 | && (instrument[i][MS_ARPEGGIO] < 0 || ((instrument_macro[MS_ARPEGGIO][num_macro_arp].length < decay_index) && 391 | (instrument_macro[MS_ARPEGGIO][num_macro_arp].loop == -1))) 392 | && (instrument[i][MS_DUTY] < 0 || ((instrument_macro[MS_DUTY][num_macro_duty].length < decay_index) && 393 | (instrument_macro[MS_DUTY][num_macro_duty].loop == -1)))) { 394 | // if a decay can be used, cut off the volume envelope at the decay point and write the decay command 395 | macro.sequence[decay_index] = decay_volume; 396 | macro.length = decay_index + 1; 397 | fprintf(file, " decay %i\n", decay_rate); 398 | } 399 | fprintf(file, " volume "); 400 | write_macro(file, ¯o); 401 | } 402 | if(instrument[i][MS_DUTY] >= 0) { 403 | fprintf(file, " timbre "); 404 | write_macro(file, &instrument_macro[MS_DUTY][num_macro_duty]); 405 | } 406 | if(instrument[i][MS_ARPEGGIO] >= 0) { 407 | ftmacro *macro = &instrument_macro[MS_ARPEGGIO][num_macro_arp]; 408 | fprintf(file, " pitch "); 409 | 410 | if(flags & ABSOLUTE_PITCH) { // Pently sfx pitch envelopes require music notes, not semitone numbers 411 | int j; 412 | for(j=0; jlength; j++) { 413 | if(j == macro->loop) 414 | fprintf(file, "| "); 415 | // convert to note 416 | int semitones = macro->sequence[j]; 417 | char note; 418 | uint8_t octave; 419 | semitone_to_note(semitones, ¬e, &octave); 420 | // print it 421 | fprintf(file, "%c%s", tolower(note), isupper(note)?"#":""); 422 | write_octave(file, octave); 423 | fputc(' ', file); 424 | } 425 | fprintf(file, "\n"); 426 | } else { 427 | write_macro(file, macro); 428 | } 429 | } 430 | } 431 | 432 | // converts the number of rows to a Pently note duration 433 | void write_duration(FILE *file, int duration, int slur) { 434 | const char *long_duration[] = { 435 | /* 1 */ "16", 436 | /* 2 */ "8", 437 | /* 3 */ "8 w16", 438 | /* 4 */ "4", 439 | /* 5 */ "4 w16", 440 | /* 6 */ "4 w8", 441 | /* 7 */ "4 w8 w16", 442 | /* 8 */ "2", 443 | /* 9 */ "2 w16", 444 | /*10 */ "2 w8", 445 | /*11 */ "2 w8 w16", 446 | /*12 */ "2 w4", 447 | /*13 */ "2 w4 w16", 448 | /*14 */ "2 w4 w8", 449 | /*15 */ "2 w4 w8 w16", 450 | /*16 */ "1" 451 | }; 452 | const char *dotted_duration[] = { 453 | /* 1 */ "16", 454 | /* 2 */ "8", 455 | /* 3 */ "8.", 456 | /* 4 */ "4", 457 | /* 5 */ "4 w16", 458 | /* 6 */ "4.", 459 | /* 7 */ "4. w16", 460 | /* 8 */ "2", 461 | /* 9 */ "2 w16", 462 | /*10 */ "2 w8", 463 | /*11 */ "2 w8.", 464 | /*12 */ "2.", 465 | /*13 */ "2. w16", 466 | /*14 */ "2. w8", 467 | /*15 */ "2. w8.", 468 | /*16 */ "1" 469 | }; 470 | const char **durations = dotted_durations ? dotted_duration : long_duration; 471 | 472 | duration--; 473 | fprintf(file, "%s%s ", durations[duration%16], slur?"~":""); 474 | while(duration > 16) { 475 | fprintf(file, "w1 "); 476 | duration -= 16; 477 | } 478 | } 479 | 480 | // write a time in the format "at" takes 481 | void write_time(FILE *file, int rows) { 482 | int measure = rows / 16; 483 | int beat = (rows % 16) / 4; 484 | int row = (rows % 16) % 4; 485 | 486 | fprintf(file, "%i", measure+1); 487 | if(beat || row) { 488 | fprintf(file, ":%i:%i", beat+1, row); 489 | } 490 | } 491 | 492 | // writes a tempo 493 | void write_tempo(FILE *file, int speed, int tempo) { 494 | float real_tempo = 6; 495 | real_tempo /= speed; 496 | real_tempo *= tempo; 497 | fprintf(file, " tempo %.2f", real_tempo); 498 | } 499 | 500 | // writes a pattern to the output file 501 | void write_pattern(FILE *file, int id, int channel) { 502 | // skip over noise channel if auto_noise and auto_dual_drums are both off 503 | // skip over DPCM channel if auto_noise or auto_dual_drums are on 504 | if((channel == CH_NOISE && !(auto_noise || auto_dual_drums)) || 505 | (channel == CH_DPCM && (auto_noise || auto_dual_drums))) 506 | return; 507 | 508 | ftnote *pattern = xsong.pattern[id][channel]; 509 | int i, slur = 0, delay_cut = 0; 510 | 511 | // find the instrument used for the pattern 512 | int instrument = -1; 513 | for(i=0; i= 0) { 515 | instrument = pattern[i].instrument; 516 | break; 517 | } 518 | if(instrument == -1) 519 | error(1, "note with no instrument %s", error_location(&xsong, channel, id, -1)); 520 | 521 | // generate pattern name and specify absolute octaves 522 | fprintf(file, "\n pattern pat_%i_%i_%i", song_num, channel, id); 523 | if(channel_is_pitched(channel)) 524 | fprintf(file, " with %s on %s\n absolute", instrument_name[instrument], chan_name[channel]); 525 | fprintf(file, "\n "); 526 | 527 | // for each row 528 | int row = 0; 529 | while(row < xsong.pattern_length[id][channel]) { 530 | char this_note = pattern[row].note; 531 | int next, octave = pattern[row].octave; 532 | 533 | // find the next note 534 | for(next = row+1; next < xsong.pattern_length[id][channel]; next++) 535 | if(pattern[next].note || pattern[next].volume) 536 | break; 537 | // the distance between this note and the next note is the duration 538 | int duration = next-row; 539 | 540 | // write any instrument changes 541 | if(isalnum(this_note) && pattern[row].instrument >= 0 && pattern[row].instrument != instrument) { 542 | instrument = pattern[row].instrument; 543 | if(channel_is_pitched(channel)) 544 | fprintf(file, "@%s ", instrument_name[instrument]); 545 | } 546 | 547 | // write volume changes 548 | if(pattern[row].volume) { 549 | switch(pattern[row].volume) { 550 | case VOL_FF: 551 | fprintf(file, "ff "); 552 | break; 553 | case VOL_MF: 554 | fprintf(file, "mf "); 555 | break; 556 | case VOL_MP: 557 | fprintf(file, "mp "); 558 | break; 559 | case VOL_PP: 560 | fprintf(file, "pp "); 561 | break; 562 | } 563 | } 564 | 565 | // handle any effects 566 | for(i=0; i= 0x08) { 676 | volume -= decay; 677 | value = ((volume+8)>>4); 678 | decay_envelope[i][j][index++] = value; 679 | } 680 | if(value != 0) 681 | decay_envelope[i][j][index++] = 0; 682 | } 683 | } 684 | 685 | // read arguments 686 | for(i=1; i=0; j--) 804 | if(song.pattern[song.pattern_id][channel][j].volume) { 805 | last_volume = song.pattern[song.pattern_id][channel][j].volume; 806 | break; 807 | } 808 | if(volume != last_volume) 809 | note.volume = volume; 810 | } 811 | 812 | if(line[2] == '=') { // note releases are not supported, so degrade to note cut or just nothing 813 | if(channel_is_pitched(channel)) 814 | note.note = '-'; 815 | else 816 | note.note = 0; 817 | } else if(line[2] != '.') { // will catch note cuts too 818 | // sharp note are uppercase 819 | note.note = (line[3]=='#')?toupper(line[2]):tolower(line[2]); 820 | // octave will be garbage for note cuts and noise notes, but that's OK 821 | note.octave = line[4]-'0'; 822 | 823 | // read instrument if it's there 824 | if(isalnum(note.note) && line[6] != '.') { 825 | int read_instrument = strtol(line+6, NULL, 16); 826 | if(read_instrument < 0 || read_instrument >= MAX_INSTRUMENTS) { 827 | error(0, "instrument (%i) out of range - %s", read_instrument, error_location(&song, channel, song.pattern_id, row)); 828 | // skip this note altogether 829 | continue; 830 | } 831 | // mark used if the note's not ignored (I should just probably actually bail out of parsing the note if it's ignored) 832 | if(channel_is_pitched(channel) && !(read_instrument != -1 && instrument_ignore[read_instrument] & (1 << channel))) 833 | instrument_used[read_instrument] = 1; 834 | note.instrument = read_instrument; 835 | } else { // if it's not, go back and find it 836 | for(j=row-1; j>=0; j--) 837 | if(song.pattern[song.pattern_id][channel][j].note && (song.pattern[song.pattern_id][channel][j].instrument != -1)) { 838 | note.instrument = song.pattern[song.pattern_id][channel][j].instrument; 839 | break; 840 | } 841 | } 842 | } 843 | 844 | // read effects 845 | for(j=0; j= 0; k--) // find previous note 867 | if(song.pattern[song.pattern_id][channel][k].note) { 868 | song.pattern[song.pattern_id][channel][k].slur = 1; 869 | break; 870 | } 871 | break; 872 | // mark the note as a slur and make the note to slur into 873 | case FX_SLUR_UP: 874 | note.slur = 1; 875 | *next_note = make_note(note.octave, note.note, note.instrument); 876 | shift_semitones(next_note, note.param[j]&15); 877 | break; 878 | case FX_SLUR_DN: 879 | note.slur = 1; 880 | *next_note = make_note(note.octave, note.note, note.instrument); 881 | shift_semitones(next_note, -(note.param[j]&15)); 882 | break; 883 | // loops, pattern cuts and fines all reduce the length of the pattern 884 | case FX_LOOP: 885 | song.loop_to = note.param[j]; 886 | goto pattern_cut; 887 | case FX_FINE: 888 | song.loop_to = -1; 889 | case FX_PAT_CUT: 890 | pattern_cut: 891 | song.pattern_length[song.pattern_id][channel] = row+1; 892 | } 893 | } 894 | 895 | // write the note only if the instrument is not ignored 896 | if(!(note.instrument != -1 && instrument_ignore[note.instrument] & (1 << channel))) { 897 | // finally write the note we made into the pattern 898 | song.pattern[song.pattern_id][channel][row] = note; 899 | } 900 | } 901 | 902 | } 903 | 904 | else if(starts_with(buffer, "TITLE ", &arg)) { 905 | char *temp = strchr(arg, '\"'); 906 | if(temp) { 907 | arg = temp+1; 908 | } 909 | fprintf(output_file, "\ntitle %s", arg); 910 | } 911 | else if(starts_with(buffer, "AUTHOR ", &arg)) { 912 | char *temp = strchr(arg, '\"'); 913 | if(temp) { 914 | arg = temp+1; 915 | } 916 | fprintf(output_file, "\nauthor %s", arg); 917 | } 918 | else if(starts_with(buffer, "COPYRIGHT ", &arg)) { 919 | char *temp = strchr(arg, '\"'); 920 | if(temp) { 921 | arg = temp+1; 922 | } 923 | fprintf(output_file, "\ncopyright %s\n", arg); 924 | } 925 | 926 | // comments are used for song metadata 927 | else if(starts_with(buffer, "COMMENT ", &arg)) { 928 | remove_line_ending(buffer, '\r'); 929 | if(*arg == '\"') 930 | arg++; 931 | char *arg2; 932 | if(starts_with(arg, "ignore ", &arg2)) { // ignore instruments on specific channels 933 | int instrument_id = 0; 934 | int channel_id = 0; 935 | 936 | // separate the channel name and instrument ID 937 | char *space = strchr(arg2, ' '); 938 | if(!space) 939 | error(1, "'ignore' takes two parameters"); 940 | *space = 0; 941 | space = skip_to_number(space+1); 942 | if(!isxdigit(*space)) 943 | error(1, "'ignore' needs an instrument number in hex"); 944 | instrument_id = strtol(space, NULL, 16); 945 | 946 | while(strcmp(chan_name[channel_id], arg2) && channel_id != CHANNEL_COUNT) 947 | channel_id++; 948 | if(channel_id == CHANNEL_COUNT) 949 | error(1, "'ignore' needs a channel name; use pulse1, pulse2, triangle, noise, drum, or attack"); 950 | 951 | printf("ignoring %x on %s\n", instrument_id, chan_name[channel_id]); 952 | instrument_ignore[instrument_id] |= 1 << channel_id; 953 | } 954 | if(starts_with(arg, "include ", &arg2)) { 955 | // import another file into this file 956 | FILE *included = fopen(arg2, "rb"); 957 | if(!included) 958 | error(1,"couldn't open included file \"%s\"", arg2); 959 | while(!feof(included)) { 960 | char c = fgetc(included); 961 | if(c != EOF) 962 | fputc(c, output_file); 963 | } 964 | fclose(included); 965 | } else if(!strcmp(arg, "auto noise")) { 966 | auto_noise = 1; 967 | } else if(!strcmp(arg, "auto dual drums")) { 968 | auto_dual_drums = 1; 969 | } else if(!strcmp(arg, "tri sxx to cut")) { 970 | tri_sxx_to_cut = 1; 971 | } else if(!strcmp(arg, "auto decay")) { 972 | decay_enabled = 1; 973 | } else if(starts_with(arg, "sfx ", &arg2)) { 974 | // define a sound effect using an instrument 975 | soundeffects[sfx_num].instrument = strtol(arg2, &arg2, 16); 976 | // skip to channel 977 | while(*arg2 == ' ') 978 | arg2++; 979 | // select the channel 980 | char channel = *(arg2++); 981 | if(channel == 's') 982 | channel = CH_SQUARE1; 983 | else if(channel == 'n') 984 | channel = CH_NOISE; 985 | else if(channel == 't') 986 | channel = CH_TRIANGLE; 987 | soundeffects[sfx_num].channel = channel; 988 | 989 | // skip to name 990 | while(*arg2 == ' ') 991 | arg2++; 992 | strlcpy(soundeffects[sfx_num].name, arg2, 64); 993 | sfx_num++; 994 | } else if(starts_with(arg, "drumsfx ", &arg2)) { 995 | // define a drum using sound effects 996 | fprintf(output_file, "drum %s\n", arg2); 997 | } else if(starts_with(arg, "drum ", &arg2)) { 998 | // drum = assign a drum to a DPCM note 999 | char *note = strchr(scale, tolower(arg2[0])); 1000 | if(!note) 1001 | error(1,"invalid note in drum definition (%c)"); 1002 | char *octave_ptr = arg2+1; 1003 | if(*octave_ptr == '#') 1004 | note++; 1005 | if(!isdigit(*octave_ptr)) 1006 | octave_ptr++; 1007 | int octave = *octave_ptr-'0'; 1008 | check_range("drum octave", octave, 0, NUM_OCTAVES, NULL); 1009 | strlcpy(drum_name[octave][note-scale], octave_ptr+2, 16); 1010 | } 1011 | } 1012 | 1013 | else if(starts_with(buffer, "COLUMNS ", &arg)) { 1014 | arg = skip_to_number(arg); 1015 | for(i=0;*arg && (i < CHANNEL_COUNT);i++) 1016 | song.effect_columns[i] = strtol(arg, &arg, 10); 1017 | } 1018 | 1019 | else if(starts_with(buffer, "MACRO ", &arg)) { 1020 | int setting = strtol(arg, &arg, 10); 1021 | check_range("macro setting type", setting, 0, MACRO_SET_COUNT, NULL); 1022 | int id = strtol(arg, &arg, 10); 1023 | check_range("macro id", id, 0, MAX_INSTRUMENTS, NULL); 1024 | instrument_macro[setting][id].loop = strtol(arg, &arg, 10); 1025 | instrument_macro[setting][id].release = strtol(arg, &arg, 10); 1026 | instrument_macro[setting][id].length = 0; 1027 | instrument_macro[setting][id].arp_type = strtol(arg, &arg, 10); 1028 | arg = skip_to_number(arg); 1029 | 1030 | // read all the numbers and count them 1031 | while(*arg) { 1032 | instrument_macro[setting][id].sequence[instrument_macro[setting][id].length++] = strtol(arg, &arg, 10); 1033 | if(instrument_macro[setting][id].length >= MAX_MACRO_LEN) 1034 | error(1,"instrument \"%s\" has a %s envelope that's too long (max length is %i)", instrument_name[id], envelope_types[setting], MAX_MACRO_LEN); 1035 | } 1036 | 1037 | // if auto decay is enabled and this is a volume envelope, try to find a decay envelope 1038 | if(decay_enabled && setting == MS_VOLUME && instrument_macro[setting][id].loop == -1 && 1039 | !instrument_macro[setting][id].sequence[instrument_macro[setting][id].length-1]) { 1040 | 1041 | int stop = 0; 1042 | int length_envelope = instrument_macro[setting][id].length-1; // length in bytes, including the zero so -1 1043 | for(i=MAX_DECAY_START-1;i>=2 && !stop; i--) // try starting volumes in reverse order 1044 | for(j=0; jeffect[fx] == FX_TEMPO) { 1210 | if(note->param[fx] < 0x20) 1211 | speed = note->param[fx]; 1212 | else 1213 | tempo = note->param[fx]; 1214 | } else if(note->effect[fx] == FX_ATTACK_ON && j == CH_ATTACK) 1215 | attack = note->param[fx]; 1216 | } 1217 | if(speed||tempo||(attack>=0)) { 1218 | if(row) { 1219 | fprintf(output_file, "\n at "); 1220 | write_time(output_file, total_rows+row); 1221 | } 1222 | if(speed||tempo) { 1223 | fprintf(output_file, "\n"); 1224 | write_tempo(output_file, speed?speed:xsong.speed, tempo?tempo:xsong.tempo); 1225 | } 1226 | if(attack>=0) { 1227 | fprintf(output_file, "\n attack on %s", chan_name[attack]); 1228 | } 1229 | } 1230 | } 1231 | total_rows += min_length; 1232 | } 1233 | fprintf(output_file, "\n at "); 1234 | write_time(output_file, total_rows); 1235 | fprintf(output_file, "\n "); 1236 | if(xsong.loop_to != -1) 1237 | fprintf(output_file, "dal segno"); 1238 | else 1239 | fprintf(output_file, "fine"); 1240 | 1241 | need_song_export = 0; 1242 | } 1243 | 1244 | if(end_of_file) 1245 | break; 1246 | } 1247 | 1248 | // write automatic noise instruments if needed 1249 | if(auto_noise) 1250 | for(i=0; i= MAX_INSTRUMENTS) { 1261 | // no arpeggio set, so make one 1262 | ftmacro new_macro = {1, -1, -1, 0, {0}, 0, 0, 0}; 1263 | instrument_macro[MS_ARPEGGIO][MAX_INSTRUMENTS-1] = new_macro; 1264 | instrument[i][MS_ARPEGGIO] = MAX_INSTRUMENTS-1; 1265 | num_macro_arp = MAX_INSTRUMENTS - 1; 1266 | } 1267 | int k; 1268 | if(num_macro_duty < MAX_INSTRUMENTS) { 1269 | // if duty is used, wrap all duty values to 0 and 1 1270 | // we don't need to worry about saving and restoring because the macros 1271 | // won't be needed after the automatic noise drums are written 1272 | ftmacro *duty_macro = &instrument_macro[MS_DUTY][num_macro_duty]; 1273 | for(k=0; klength; k++) 1274 | duty_macro->sequence[k] &= 1; 1275 | } 1276 | 1277 | ftmacro *arp_macro = &instrument_macro[MS_ARPEGGIO][num_macro_arp]; 1278 | ftmacro old = *arp_macro; 1279 | for(k=0; klength; k++) 1280 | arp_macro->sequence[k] = (arp_macro->sequence[k]+j)&15; 1281 | write_instrument(output_file, i, 0); // disallow decay 1282 | *arp_macro = old; 1283 | 1284 | // define a drum for the frequency 1285 | fprintf(output_file, "\ndrum %s_%x_ noise_%s_%x", instrument_name[i], j, instrument_name[i], j); 1286 | } 1287 | 1288 | // close files 1289 | fclose(input_file); 1290 | fprintf(output_file, "\n\n"); 1291 | fclose(output_file); 1292 | 1293 | return 0; 1294 | } 1295 | --------------------------------------------------------------------------------