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