Created
July 3, 2025 00:08
-
-
Save cs127/ad37bcc9dda95e0c2487033487545afe to your computer and use it in GitHub Desktop.
NES DMC sample to WAVE converter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// dmdecode v0.00 | |
// extremely simple CLI tool for converting | |
// Nintendo DMC (1-to-7-bit mono DPCM) samples to 8-bit PCM WAVE | |
// | |
// C99 (or newer) required! | |
// | |
// cs127 | |
// https://cs127.github.io | |
// 2025-07-02 | |
#define INITIAL_VALUE 0x40 | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#define eprintf(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) | |
#define fgoto(fp, n) fseek(fp, n, SEEK_SET) | |
#define fskip(fp, n) fseek(fp, n, SEEK_CUR) | |
void fputu16(uint16_t v, FILE* fp) | |
{ | |
fputc(v >> 0 & 0xFF, fp); | |
fputc(v >> 8 & 0xFF, fp); | |
} | |
void fputu32(uint32_t v, FILE* fp) | |
{ | |
fputc(v >> 0 & 0xFF, fp); | |
fputc(v >> 8 & 0xFF, fp); | |
fputc(v >> 16 & 0xFF, fp); | |
fputc(v >> 24 & 0xFF, fp); | |
} | |
int main(int argc, char** argv) | |
{ | |
register int inv; | |
register uint8_t outv = INITIAL_VALUE; | |
register size_t i; | |
int ret = 0; | |
long int riff_start, data_start, file_end; | |
FILE* in = NULL, * out = NULL; | |
if (argc != 3) | |
{ | |
eprintf("usage: %s INFILE OUTFILE\n", argv[0]); | |
ret = 1; | |
goto end; | |
} | |
in = fopen(argv[1], "rb"); | |
if (!in) | |
{ | |
eprintf("could not open file `%s' for reading.\n", argv[1]); | |
ret = 3; | |
goto end; | |
} | |
out = fopen(argv[2], "wb"); | |
if (!out) | |
{ | |
eprintf("could not open file `%s' for writing.\n", argv[2]); | |
ret = 4; | |
goto end; | |
} | |
// main RIFF file header | |
fputs("RIFF", out); | |
fskip(out, 4); // chunk size (written later) | |
riff_start = ftell(out); | |
fputs("WAVE", out); // file type | |
// fmt chunk | |
fputs("fmt ", out); | |
fputu32(16, out); // chunk size | |
fputu16(1, out); // format (1 = integer PCM) | |
fputu16(1, out); // channel count (1 = mono) | |
fputu32(33144, out); // sample rate, i.e. sampling points per second | |
fputu32(33144, out); // bytes per second | |
fputu16(1, out); // bytes per sampling point | |
fputu16(8, out); // bit depth | |
// data chunk | |
fputs("data", out); | |
fskip(out, 4); // chunk size (written later) | |
data_start = ftell(out); | |
while ((inv = fgetc(in)) != EOF) | |
{ | |
for (i = 0; i < 8; i++) | |
{ | |
if (inv & (1 << i)) | |
{ | |
if (outv < 0x7F) outv++; | |
} | |
else | |
{ | |
if (outv > 0x00) outv--; | |
} | |
fputc(outv * 2, out); | |
} | |
} | |
file_end = ftell(out); | |
// go back and write the chunk sizes | |
fgoto(out, riff_start - 4); | |
fputu32(file_end - riff_start, out); | |
fgoto(out, data_start - 4); | |
fputu32(file_end - data_start, out); | |
if (ferror(out)) | |
{ | |
eprintf("could not successfully write to `%s'.\n", argv[2]); | |
ret = 6; | |
goto end; | |
} | |
end: | |
if (in) fclose(in); | |
if (out) fclose(out); | |
return ret; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment