/* * modrender.c - renderiza um modulo de tracker (.s3m, .xm, .mod, .it) em WAV PCM 16 bits estereo, 44100 Hz, ate o * fim da musica (ou ate o primeiro ponto de repeticao), sempre com os mesmos bytes. Usa a libxmp. * Escrito para o fredygames.com (musicas dos jogos de pygame: o SDL_mixer do pygbag nao toca modulos). * Copyright (C) 2026 FredyGames. Licenca: MIT. * * modrender entrada.xm saida.wav [segundos-maximos] */ #include #include #include #include static void put32(unsigned char *p, unsigned v) { p[0] = v; p[1] = v >> 8; p[2] = v >> 16; p[3] = v >> 24; } int main(int argc, char **argv) { if (argc < 3) { fprintf(stderr, "uso: modrender entrada saida.wav [segundos]\n"); return 2; } double max_seconds = argc > 3 ? atof(argv[3]) : 600; xmp_context ctx = xmp_create_context(); if (xmp_load_module(ctx, argv[1]) < 0) { fprintf(stderr, "nao consegui carregar %s\n", argv[1]); return 1; } const int rate = 44100; if (xmp_start_player(ctx, rate, 0) < 0) { fprintf(stderr, "erro no player\n"); return 1; } FILE *out = fopen(argv[2], "wb"); if (!out) { perror(argv[2]); return 1; } unsigned char hdr[44] = {0}; fwrite(hdr, 1, 44, out); unsigned long bytes = 0, limit = (unsigned long)(max_seconds * rate) * 4; struct xmp_frame_info fi; for (;;) { if (xmp_play_frame(ctx) != 0) break; xmp_get_frame_info(ctx, &fi); if (fi.loop_count > 0) break; fwrite(fi.buffer, 1, fi.buffer_size, out); bytes += fi.buffer_size; if (bytes >= limit) break; } memcpy(hdr, "RIFF", 4); put32(hdr + 4, 36 + bytes); memcpy(hdr + 8, "WAVEfmt ", 8); put32(hdr + 16, 16); hdr[20] = 1; hdr[22] = 2; put32(hdr + 24, rate); put32(hdr + 28, rate * 4); hdr[32] = 4; hdr[34] = 16; memcpy(hdr + 36, "data", 4); put32(hdr + 40, bytes); fseek(out, 0, SEEK_SET); fwrite(hdr, 1, 44, out); fclose(out); xmp_end_player(ctx); xmp_release_module(ctx); xmp_free_context(ctx); fprintf(stderr, "%s: %.1f s\n", argv[2], bytes / 4.0 / rate); return 0; }