Rauser-Fade-Bug (Alpha-Cutout), Musik-Laenge-Bug (real_song_end-Erkennung) und Factor5-Melodie gefixt
This commit is contained in:
@@ -62,10 +62,23 @@ static void write_wav(const char *path, int16_t *pcm, int32_t frames, int32_t sa
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 4) {
|
||||
fprintf(stderr, "usage: %s <mdat.tfx> <smpl.sam> <out.wav> [seconds]\n", argv[0]);
|
||||
fprintf(stderr, "usage: %s <mdat.tfx> <smpl.sam> <out.wav> [max_seconds]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
double seconds = argc > 4 ? atof(argv[4]) : 15.0;
|
||||
// WICHTIG (Fix 22.07.2026 abends): [max_seconds] ist NUR noch eine
|
||||
// Sicherheits-Obergrenze, keine Ziel-Laenge mehr! Vorher wurde exakt
|
||||
// `seconds` lang gerendert -- war der Wert kleiner als die echte
|
||||
// Songlaenge, wurde mitten im Stueck abgeschnitten (Stefans Meldung:
|
||||
// "die Musik ist zu kurz, da fehlt der Rest", verursacht durch TITEL mit
|
||||
// festen 60s bzw. einen stehengebliebenen 8s-Cache-Rest). Stattdessen
|
||||
// rendern wir jetzt in kleinen Haeppchen und beobachten tfmx.h's eigenes
|
||||
// `real_song_end`-Flag (siehe tfmx.h: wird pro Tick in tfmx_run()
|
||||
// gesetzt, sobald die Track-Sequenz einmal komplett durchgelaufen ist
|
||||
// und intern neu startet) -- sobald es feuert, haben wir GENAU eine
|
||||
// vollstaendige natuerliche Song-Schleife im Kasten und brechen ab,
|
||||
// egal wie lang das Stueck wirklich ist. max_seconds greift nur als
|
||||
// Notbremse, falls ein Modul (defekt/kaputt) nie ein Ende meldet.
|
||||
double max_seconds = argc > 4 ? atof(argv[4]) : 180.0;
|
||||
|
||||
uint32_t mdat_len = 0, smpl_len = 0;
|
||||
uint8_t *mdat = load_file(argv[1], &mdat_len);
|
||||
@@ -88,20 +101,61 @@ int main(int argc, char **argv) {
|
||||
memcpy(bundle + hdr_off + mdat_len, smpl, smpl_len);
|
||||
|
||||
int32_t sample_rate = 44100;
|
||||
void *s = tfmx_api.init(bundle, bundle_len, sample_rate);
|
||||
// Direkt tfmx_init/tfmx_get_audio statt der generischen player_api-
|
||||
// Indirektion nutzen -- wir brauchen Zugriff auf state->real_song_end,
|
||||
// das die generische Schnittstelle (nur get_audio(state, out, frames))
|
||||
// nicht durchreicht.
|
||||
struct tfmx_state *s = tfmx_init(bundle, bundle_len, sample_rate);
|
||||
if (!s) {
|
||||
fprintf(stderr, "tfmx_api.init failed (module not recognized)\n");
|
||||
fprintf(stderr, "tfmx_init failed (module not recognized)\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
int32_t total_frames = (int32_t)(seconds * sample_rate);
|
||||
float *scratch = malloc(sizeof(float) * total_frames * 2);
|
||||
int16_t *pcm = malloc(sizeof(int16_t) * total_frames * 2);
|
||||
player_get_audio_s16(&tfmx_api, s, pcm, scratch, total_frames);
|
||||
int32_t max_frames = (int32_t)(max_seconds * sample_rate);
|
||||
// Chunk klein genug waehlen, dass real_song_end nicht durch einen
|
||||
// zweiten Tick INNERHALB desselben Chunks schon wieder auf 0 zurueck-
|
||||
// gesetzt wurde, bevor wir nachsehen (ein Tick liegt typischerweise bei
|
||||
// ~20ms/882 Frames bei 44.1kHz -- 64 Frames sind davon weit entfernt).
|
||||
const int32_t CHUNK = 64;
|
||||
float *scratch = malloc(sizeof(float) * CHUNK * 2);
|
||||
int16_t *pcm = malloc(sizeof(int16_t) * (size_t)max_frames * 2);
|
||||
if (!scratch || !pcm) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
write_wav(argv[3], pcm, total_frames, sample_rate);
|
||||
fprintf(stderr, "wrote %s: %d frames @ %d Hz (%.1fs)\n", argv[3], total_frames, sample_rate, seconds);
|
||||
int32_t total = 0;
|
||||
int natural_end = 0;
|
||||
while (total < max_frames) {
|
||||
int32_t this_chunk = CHUNK;
|
||||
if (total + this_chunk > max_frames) {
|
||||
this_chunk = max_frames - total;
|
||||
}
|
||||
|
||||
tfmx_api.free(s);
|
||||
memset(scratch, 0, sizeof(float) * (size_t)this_chunk * 2);
|
||||
tfmx_get_audio(s, scratch, this_chunk);
|
||||
|
||||
int16_t *out_ptr = pcm + (size_t)total * 2;
|
||||
for (int32_t i = 0; i < this_chunk * 2; ++i) {
|
||||
float v = scratch[i] * 32767.0f;
|
||||
if (v > 32767.0f) v = 32767.0f;
|
||||
if (v < -32768.0f) v = -32768.0f;
|
||||
out_ptr[i] = (int16_t)v;
|
||||
}
|
||||
total += this_chunk;
|
||||
|
||||
if (s->real_song_end) {
|
||||
natural_end = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
write_wav(argv[3], pcm, total, sample_rate);
|
||||
fprintf(stderr, "wrote %s: %d frames @ %d Hz (%.2fs)%s\n",
|
||||
argv[3], total, sample_rate, (double)total / sample_rate,
|
||||
natural_end ? " [natuerliches Songende/Loop-Punkt erkannt]"
|
||||
: " [Sicherheits-Obergrenze erreicht, kein Songende gefunden -- Modul pruefen]");
|
||||
|
||||
tfmx_free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user