Zvleon
Usuario de oro
Primero que todo, quiero dejar claro unos puntos importantes:
Paso 0: Nociones básicas
Es importante tener en cuenta como funcionan los instrumentos en un MIDI:
Edit(25/09/2020): A pedido de Jaizu voy a ahondar un poco más en esto para que se entienda mejor. Como ejemplo tomaré a
Paso 4: Darle a la canción un ID y un nombre
Paso 5: Definir la ruta
Con todo esto su MIDI ya debería estar listo y podrán compilar sin problemas. Si siguieron todo como corresponde, la canción debería poder usarse desde Porymap o ser cambiada en batallas también. Adjunto algunos ejemplos de cosas que he hecho con esto:
Espero que el tutorial sea de utilidad para alguien y que el Jaizu me deje en paz con los MIDIs. Trataré de estar atento por si me falta algo o si alguien no entiende algo
- En este tutorial no se va a explicar como arreglar MIDIs o que programas utilizar. Se asume que el lector tiene el MIDI listo para ser insertado.
- No manejo toda la información del tema, pero el plan es darle soporte e ir actualizando a medida que descubra más cosas o por ciertas actualizaciones de pokeemerald algunos pasos dejen de funcionar.
- Es mi primer post en el foro, y tampoco es que sea experto en el tema, así que probablemente se me van a pasar cosas por alto o diré cosas erradas. Pido paciencia (?)
- Si, soy Trickster :^)
Paso 0: Nociones básicas
Es importante tener en cuenta como funcionan los instrumentos en un MIDI:
- Los instrumentos se encuentran en un banco de instrumentos que está asociado a lo que sea que está reproduciendo el MIDI (véase FL Studio o el mismo reproductor multimedia de Windows) y normalmente estos están ordenados. Por ejemplo, los primeros 20 son instrumentos de tecla, como pianos, órganos, xilófonos, etc. Los bronces y vientos están entre el 56-79 y así.
- Cada canal/track del MIDI tiene asociado un instrumento o voz, y esta voz puede cambiar en el transcurso de la canción mediante eventos. Por ejemplo, el track de la bassline puede ir alternando entre pick bass y slap bass. Esto escapa al alcance del tutorial, pero creo que es importante tenerlo en cuenta.
Para esto basta con agarrar el MIDI y copiarlo en
./sound/songs/midi
. Aunque esto no basta para hacerlo funcional en el juego.
Paso 2: Reportarle al compilador la existencia del archivoEn la carpeta raíz de pokeemerald se encuentra el archivo
songs.mk
.Para poder definir el MIDI dentro del archivo seguimos la siguiente estructura:
Makefile:
$(MID_SUBDIR)/nombre_midi.s: %.s: %.mid[/INDENT][/INDENT]
[INDENT][INDENT](MID) $< $@ -E -R$(STD_REVERB) -GXXX -V090
Donde GXXX corresponde al VOICEGROUP que usará la canción. Algunos se preguntarán ¿Que es el voicegroup? Lo veremos en siguiente punto pero básicamente, es como el banco de instrumentos que mencionamos en el paso 0.
Paso 3: Definir el VOICEGROUPEl voicegroup como se mencionó antes, es una suerte de banco de instrumentos, solo que este no los contiene, es una referencia. La manera en la que se define es la siguiente: Si en mi MIDI usé el instrumento 17(Percussion Organ) entonces tengo que definirlo en el slot 17 del voicegroup.
Pro Tip: Si editan esto con cualquier editor de texto somo Sublime Text o VS Code, entonces toman el ID en el banco de instrumentos (en este caso, el 17) y le suman 3. El resultado representa el número de línea en el editor de texto donde debería ir Percussion Organ.
A continuación les dejo un ejemplo de voicegroup que podrían usar ustedes. Contiene la mayoría de los instrumentos disponibles en el juego.
Enriquecido (Código BB):
.align 2
voicegroup196:: @ 86A8CBC
voice_keysplit_all voicegroup002 @ 86A8CBC
voice_keysplit voicegroup005, KeySplitTable1 @ 86A8CC8
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8CD4
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8CE0
voice_directsound 60, 0, DirectSoundWaveData_sd90_classical_detuned_ep1_low, 255, 249, 0, 165 @ 86A8CEC
voice_directsound 60, 0, DirectSoundWaveData_sd90_classical_detuned_ep1_high, 255, 188, 103, 165 @ 86A8CF8
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8D04
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8D10
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8D1C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8D28
voice_directsound 60, 0, DirectSoundWaveData_heart_of_asia_gamelan, 255, 188, 139, 239 @ 86970E8
voice_directsound 60, 0, DirectSoundWaveData_sc88_glockenspiel, 255, 188, 103, 165
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8D4C
voice_directsound 60, 0, DirectSoundWaveData_sc88_xylophone, 255, 188, 103, 165
voice_directsound 60, 0, DirectSoundWaveData_sc88_tubular_bell, 255, 165, 90, 216 @ 86A8D64
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8D70
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8D7C
voice_directsound 60, 0, DirectSoundWaveData_sc88_organ2, 255, 0, 255, 127 @ 86A8D88
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8D94
voice_directsound 60, 0, DirectSoundWaveData_emu_ii_pipe_organ, 255, 188, 103, 165
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8DAC
voice_directsound 60, 0, DirectSoundWaveData_sc88_accordion, 255, 188, 103, 165
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8DC4
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8DD0
voice_directsound 60, 0, DirectSoundWaveData_sc88_nylon_str_guitar, 128, 249, 25, 127 @ 86A8DDC
voice_directsound 60, 0, DirectSoundWaveData_sc88_nylon_str_guitar, 255, 165, 128, 204 @ 86A8DE8
voice_directsound 60, 0, DirectSoundWaveData_unused_electric_guitar, 255, 165, 128, 204 @ 86A8DE8
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8E00
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8E0C
voice_directsound 60, 0, DirectSoundWaveData_sd90_classical_overdrive_guitar, 255, 0, 255, 127 @ 86A8E18
voice_directsound 60, 0, DirectSoundWaveData_sd90_classical_distortion_guitar_high, 255, 165, 154, 165 @ 86A8E24
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8E30
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8E3C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8E3C
voice_directsound 60, 0, DirectSoundWaveData_sc88_fretless_bass, 255, 255, 0, 200
voice_directsound 60, 0, DirectSoundWaveData_sc88_fretless_bass, 255, 255, 0, 200
voice_directsound 60, 0, DirectSoundWaveData_jv1080_slap_bass, 255, 224, 0, 204
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8E78
voice_directsound 60, 0, DirectSoundWaveData_sc88_synth_bass, 255, 165, 128, 204
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8E90
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8E9C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8EA8
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8EB4
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8EC0
voice_directsound 60, 0, DirectSoundWaveData_sc88_pizzicato_strings, 255, 165, 128, 204
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8ED8
voice_directsound 60, 0, DirectSoundWaveData_sc88_harp, 255, 165, 128, 204
voice_directsound 60, 0, DirectSoundWaveData_sc88_timpani, 255, 246, 0, 226 @ 86A8EF0
voice_keysplit voicegroup006, KeySplitTable2 @ 86A8EFC
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8F08
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8F14
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8F20
voice_directsound 60, 0, DirectSoundWaveData_classical_choir_voice_ahhs, 255, 246, 0, 226
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8F44
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8F44
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8F50
voice_keysplit voicegroup007, KeySplitTable3 @ 86A8F5C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8F68
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8F74
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8F80
voice_keysplit voicegroup009, KeySplitTable5 @ 86A8F8C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8F98
voice_directsound 60, 0, DirectSoundWaveData_sd90_classical_distortion_guitar_low, 255, 165, 180, 165 @ 86A8FA4
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8FB0
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8FBC
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8FC8
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8FD4
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8FE0
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8FEC
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A8FF8
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9004
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9010
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A901C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9028
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9034
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9040
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A904C
voice_directsound 60, 0, DirectSoundWaveData_sd90_classical_shakuhachi, 255, 246, 0, 226
voice_directsound 60, 0, DirectSoundWaveData_sd90_classical_whistle, 255, 246, 0, 226
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9070
voice_square_1_alt 0, 1, 0, 2, 7, 2 @ 86A907C
voice_square_2_alt 3, 0, 3, 6, 2 @ 86A9088
voice_square_2_alt 3, 0, 2, 6, 5 @ 86A9094
voice_programmable_wave_alt ProgrammableWaveData_86B4890, 0, 3, 6, 5 @ 86A90A0
voice_square_2_alt 0, 0, 2, 6, 5 @ 86A90AC
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A90B8
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A90C4
voice_programmable_wave_alt ProgrammableWaveData_86B4850, 0, 7, 15, 1 @ 86A90D0
voice_programmable_wave_alt ProgrammableWaveData_86B4830, 0, 7, 15, 0 @ 86A90DC
voice_programmable_wave_alt ProgrammableWaveData_86B4910, 0, 1, 9, 2 @ 86A90E8
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A90F4
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9100
voice_square_2_alt 2, 0, 2, 6, 3 @ 86A910C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9118
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9124
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9130
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A913C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9148
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9154
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9160
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A916C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9178
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9184
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9190
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A919C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A91A8
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A91B4
voice_directsound 60, 0, DirectSoundWaveData_unknown_koto_high, 255, 246, 0, 226
voice_directsound 60, 0, DirectSoundWaveData_unknown_koto_low, 255, 246, 0, 226
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A91D8
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A91E4
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A91F0
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A91FC
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9208
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9214
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9220
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A922C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9238
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9244
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9250
voice_directsound 60, 0, DirectSoundWaveData_sd90_special_scream_drive, 255, 0, 255, 165 @ 86A925C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9268
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9274
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9280
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A928C
voice_square_1 0, 2, 0, 0, 15, 0 @ 86A9298
voice_noise_alt 0, 0, 2, 6, 2 @ 86A92A4
voice_noise_alt 0, 0, 1, 6, 0 @ 86A92B0
Aquí defini el voicegroup196.inc o G196. Estos archivos están ubicados en
./sound/voicegroups/
. Este es un archivo conformado solo por macros que siguen la siguiente estructura:
Enriquecido (Código BB):
.macro voice_directsound base_midi_key, pan, sample_data_pointer, attack, decay, sustain, release
.byte 0
_voice_directsound \base_midi_key, \pan, \sample_data_pointer, \attack, \decay, \sustain, \release
.endm
.macro voice_directsound_no_resample base_midi_key, pan, sample_data_pointer, attack, decay, sustain, release
.byte 8
_voice_directsound \base_midi_key, \pan, \sample_data_pointer, \attack, \decay, \sustain, \release
.endm
.macro voice_directsound_alt base_midi_key, pan, sample_data_pointer, attack, decay, sustain, release
.byte 16
_voice_directsound \base_midi_key, \pan, \sample_data_pointer, \attack, \decay, \sustain, \release
.endm
.macro _voice_directsound base_midi_key, pan, sample_data_pointer, attack, decay, sustain, release
.byte \base_midi_key
.byte 0
.if \pan != 0
.byte (0x80 | \pan)
.else
.byte 0
.endif
.4byte \sample_data_pointer
.byte \attack
.byte \decay
.byte \sustain
.byte \release
.endm
Los más importantes son
voice_directsound
y voice_keysplit_all
. Donde el primero permite cargar un instrumento, comoDirectSoundWaveData_sc88_fretless_bass
o DirectSoundWaveData_sd90_classical_overdrive_guitar
, mientras que el segundo permite agarrar múltiples samples y asociarlos a una nota. Esto se utiliza particularmente para la batería, la cual esta conformada por distintos samples como el snare o el hi-hat. Personalmente no se si sea prudente entrar en detalles con esto porque no lo entiendo del todo, pero voicegroup002
siento que es un buen ejemplo si desean entenderlo.Edit(25/09/2020): A pedido de Jaizu voy a ahondar un poco más en esto para que se entienda mejor. Como ejemplo tomaré a
voice_direct_sound
pues como dije previamente no tengo mucho manejo con los otros:- base_midi_key: Este parámetro representa la tonalidad base del sample utilizado como instrumento. Por ejemplo, si un instrumento tiene este parámetro como 60, significa que su nota base es un DO de la quinta octava. Una octava tiene 7 notas naturales y 5 semitonos (para los que no entienden de esto, las notas naturales son las blancas de un piano, mientras que los semitonos son las negras) así que tomando el DO de la primera octava como el valor inicial y ahora sabiendo que cada octava tiene 12 notas en total, entonces tenemos
60/12 = 5
lo que corresponde a la quinta octava previamente mencionada.
¿Cuál es la utilidad de esto? Muchas veces los samples no vienen grabados en la nota Do, que es lo normal, así que hay que "transponer" la nota para que suene como corresponda, así que este parámetro en términos simples "afina" el instrumento. - pan: En los MIDIs, el pan corresponde a por donde sale el audio. Si el valor está en el rango de los negativos, significa que se escuchará más fuerte por el speaker izquierdo, y si está en el rango positivo, por el derecho. Este es un truquillo que permite darle más textura y dinamismo a una canción. Claramente esto es algo que se debe tocar en el MIDI, no en la configuración del sample. Es mejor dejarlo seteado en 0.
- sample_data_pointer: Este indica el instrumento que se utilizará en este banco del voicegroup. Si queremos usar una trompeta, entonces buscamos el nombre del sample y lo insertamos aquí, el cual sería
DirectSoundWaveData_sc88_trumpet_72
. Ahora bien, la trompeta es uno de esos instrumentos que utiliza elkeysplit
. Esto está definido en el voicegroup007, así que como recomendación, si quieren usar trompetas, entonces usenvoice_keysplit voicegroup007, KeySplitTable3
. - attack, decay, sustain y release: Estos parámetros corresponden a como el sample "toca" las notas. Con esto me refiero a la intensidad base, a cuanto se demora en sonar o cuando dejará de hacerlo. No me he metido mucho con ésto, así que personalmente recomiendo ir viendo otros voicegroups que utilicen el mismo instrumento que quieran usar y copian estos parametros.
Una cosa importante que hay que dejar en claro en este punto es que, tal y como se mencionó en puntos previos, los bancos de MIDIs suelen tener 128 instrumentos, pero este no es el caso de pokeemerald. Instrumentos como Music Box, Pipe Organ o Acoustic Bass no existen, razón por la cual existen espacios en blanco en forma de
voice_square
en el voicegroup. Tal vez alguno se preguntará si es posible añadirlos, y si, lo es. He agregado algunos samples adicionales pero necesito aprender algunas cosas extra como por ejemplo: loopear dichos samples para que funcionen como deben, así que de momento, no tengo planes de explicarlo.
Dejando todo eso de lado, una vez tengan listo su voicegroup tienen que incluirlo en la lista de todos los voicegroups que existen en el juego. Esto se encuentra en
sound/voice_groups.inc
Paso 4: Darle a la canción un ID y un nombre
Cada canción debe tener un ID, el cual es utilizado parámetro por las funciones que reproducen la canción. Esto se hace en el archivo
include/constants/songs.h
. En cuanto al nombre, nos vamos a sound/song_table.inc
. Por consistencia, debe ser el mismo nombre utilizado en songs.mk
Paso 5: Definir la ruta
Al igual que todos los archivos que existen en el juego, tienen que ser definidos en
ld_script.txt
para que el compilador pueda dar con ellos. Así que en el bloque de song_data
debemos agregar la siguiente linea:
Enriquecido (Código BB):
sound/songs/midi/nombre_midi.o(.rodata);
Con todo esto su MIDI ya debería estar listo y podrán compilar sin problemas. Si siguieron todo como corresponde, la canción debería poder usarse desde Porymap o ser cambiada en batallas también. Adjunto algunos ejemplos de cosas que he hecho con esto:
Última edición: