/* $Id: wav_write.c,v 1.4 2006/09/15 13:34:06 toad32767 Exp $ */ /** ** 2005, 2006 by Marco Trillo ** This file is part of UModPlayer, and is released by ** its autors to the Public Domain. ** In case it's not legally possible, its autors grant ** anyone the right to use, redistribute and modify ** this software for any purpose, without any conditions, ** unless such conditions are required by law. ** ** THIS FILE COMES WITHOUT ANY WARRANTY. THE AUTHORS ** SHALL NOT BE LIABLE FOR ANY DAMAGE RESULTING BY THE ** USE OR MISUSE OF THIS SOFTWARE. **/ /* * ===================== * WAV FILE WRITER * ===================== * You can see WAV file documentation at: * http://www.sonicspot.com/guide/wavefiles.html */ #include #include #define WAV_TYPE_PCM 0x0001 struct str_wavefmt { uint16_t wFormatTag; uint16_t nChannels; uint32_t nSamplesPerSec; uint32_t nAvgBytesPerSec; uint16_t nBlockAlign; uint16_t wBitsPerSample; }; EXPORT uint32_t WriteWAV(const char *rfile) { char buffer[20000]; int16_t *samples; int fd, bytes_out = 0, bytes_in = 0, upos, epos; uint32_t length = 0; struct str_wavefmt wf; uint16_t qw; uint32_t qp; int order, d, e; fd = open(rfile, O_WRONLY | O_TRUNC | O_CREAT, 0644); if (fd == -1) return 0; ModPlug_SeekOrder(file.mod, startpos); notice("Exporting audio to WAV ('%s')\n", rfile); /* Use 16-bit samples */ CoreSound_BitsPerSample = 16; CoreSound_InitOptions(); /* Fill the WAV format chunk */ wf.wFormatTag = bsLE16(WAV_TYPE_PCM); qw = (uint16_t) sets.channels; wf.nChannels = bsLE16(qw); wf.wBitsPerSample = bsLE16(16); /* blockAlign = channels * bytesPerSample = channels * 2 = channels << * 1 */ qw = qw << 1; wf.nBlockAlign = bsLE16(qw); qp = (uint32_t) sets.samplerate; wf.nSamplesPerSec = bsLE32(qp); /* bytesPerSec = samplesPerSec * channels * bytesPerSample = * samplesPerSec * blockAlign */ qp = qp * qw; wf.nAvgBytesPerSec = bsLE32(qp); /* Write WAV header We`ll fill in the blank spaces later. */ if (write(fd, "RIFF\0\0\0\0WAVEfmt \20\0\0\0", 20) < 20) { close(fd); return 0; } length += 20; /* Write WAV format chunk */ if (write(fd, (char *) &wf, 16) < 16) { close(fd); return 0; } length += 16; /* Write 'data' chunk header */ if (write(fd, "data\0\0\0\0", 8) < 8) { close(fd); return 0; } length += 8; /* * Wavedata in 16-bit samples are signed values from -32768 to +32767 (int16_t) */ samples = (int16_t *) buffer; /* * Read sample data from LibModPlug. * This data will be in system endianness. */ while ((bytes_out = ModPlug_Read(file.mod, buffer, 20000)) > 0) { length += bytes_out; /* Calculate the number of int16_t values entered in buffer */ d = bytes_out >> 1; /* bytes_out / 2 */ if (sets.endianness == BIG_ENDIAN) { /* WAV files are always Little Endian. So if we are * Big Endian, we need to convert ModPlug`s BigEndian * data into LittleEndian. */ for (e = 0; e < d; ++e) { samples[e] = swap16(samples[e]); } } /* * Write the data to the file. * If the system cant write the 20000-bytes buffer all in one, * we use many write() calls. */ upos = 0; bytes_in = bytes_out; for (;;) { epos = write(fd, buffer + upos, bytes_in); if (epos < 1) { close(fd); return 0; } if (epos == bytes_in) break; upos += epos; bytes_in -= epos; } /* * Tell the user how many bytes we wrote. */ notice("%lu bytes written \r", (unsigned long) length); fflush(stdout); /* * Check if we have to finish reading */ if (endpos > 0) { order = ModPlug_GetCurrentOrder(file.mod); if (order >= endpos) break; } } if (lseek(fd, 4, SEEK_SET) == -1) { close(fd); return 0; } qp = length - 8; /* Total WAV size except the 8 first bytes */ qp = bsLE32(qp); if (write(fd, (char *) &qp, 4) < 4) { close(fd); return 0; } if (lseek(fd, 40, SEEK_SET) == -1) { close(fd); return 0; } if (length < 45) { close(fd); return 0; } /* * sizeof samples is equal to total file length minus header length (=44) */ qp = length - 44; qp = bsLE32(qp); if (write(fd, (char *) &qp, 4) < 4) { close(fd); return 0; } close(fd); notice("\ndone!\n"); return length; }