|
|
1.1 ! root 1: /* ! 2: Hatari - wavFormat.c ! 3: ! 4: This file is distributed under the GNU Public License, version 2 or at ! 5: your option any later version. Read the file gpl.txt for details. ! 6: ! 7: WAV File output ! 8: ! 9: As well as YM file output we also have output in .WAV format. These .WAV ! 10: files can then be run through convertors to any other format, such as MP3. ! 11: We simply save out the WAVE format headers and then write the sample data ! 12: (at the current rate of playback) as we build it up each frame. When we stop ! 13: recording we complete the size information in the headers and close up. ! 14: ! 15: ! 16: RIFF Chunk (12 bytes in length total) Byte Number ! 17: 0 - 3 "RIFF" (ASCII Characters) ! 18: 4 - 7 Total Length Of Package To Follow (Binary, little endian) ! 19: 8 - 12 "WAVE" (ASCII Characters) ! 20: ! 21: FORMAT Chunk (24 bytes in length total) Byte Number ! 22: 0 - 3 "fmt_" (ASCII Characters) ! 23: 4 - 7 Length Of FORMAT Chunk (Binary, always 0x10) ! 24: 8 - 9 Always 0x01 ! 25: 10 - 11 Channel Numbers (Always 0x01=Mono, 0x02=Stereo) ! 26: 12 - 15 Sample Rate (Binary, in Hz) ! 27: 16 - 19 Bytes Per Second ! 28: 20 - 21 Bytes Per Sample: 1=8 bit Mono, 2=8 bit Stereo or 16 bit Mono, 4=16 bit Stereo ! 29: 22 - 23 Bits Per Sample ! 30: ! 31: DATA Chunk Byte Number ! 32: 0 - 3 "data" (ASCII Characters) ! 33: 4 - 7 Length Of Data To Follow ! 34: 8 - end Data (Samples) ! 35: */ ! 36: const char WAVFormat_fileid[] = "Hatari wavFormat.c : " __DATE__ " " __TIME__; ! 37: ! 38: #include <SDL_endian.h> ! 39: ! 40: #include "main.h" ! 41: #include "audio.h" ! 42: #include "configuration.h" ! 43: #include "file.h" ! 44: #include "log.h" ! 45: #include "sound.h" ! 46: #include "wavFormat.h" ! 47: ! 48: ! 49: static FILE *WavFileHndl; ! 50: static int nWavOutputBytes; /* Number of samples bytes saved */ ! 51: bool bRecordingWav = false; /* Is a WAV file open and recording? */ ! 52: ! 53: ! 54: static Uint8 WavHeader[] = ! 55: { ! 56: /* RIFF chunk */ ! 57: 'R', 'I', 'F', 'F', /* "RIFF" (ASCII Characters) */ ! 58: 0, 0, 0, 0, /* Total Length Of Package To Follow (patched when file is closed) */ ! 59: 'W', 'A', 'V', 'E', /* "WAVE" (ASCII Characters) */ ! 60: /* Format chunk */ ! 61: 'f', 'm', 't', ' ', /* "fmt_" (ASCII Characters) */ ! 62: 0x10, 0, 0, 0, /* Length Of FORMAT Chunk (always 0x10) */ ! 63: 0x01, 0, /* Always 0x01 */ ! 64: 0x02, 0, /* Number of channels (2 for stereo) */ ! 65: 0, 0, 0, 0, /* Sample rate (patched when file header is written) */ ! 66: 0, 0, 0, 0, /* Bytes per second (patched when file header is written) */ ! 67: 0x04, 0, /* Bytes per sample (4 = 16 bit stereo) */ ! 68: 0x10, 0, /* Bits per sample (16 bit) */ ! 69: /* Data chunk */ ! 70: 'd', 'a', 't', 'a', ! 71: 0, 0, 0, 0, /* Length of data to follow (will be patched when file is closed) */ ! 72: }; ! 73: ! 74: ! 75: /** ! 76: * Open WAV output file and write header. ! 77: */ ! 78: bool WAVFormat_OpenFile(char *pszWavFileName) ! 79: { ! 80: ! 81: Uint32 nSampleFreq, nBytesPerSec; ! 82: ! 83: bRecordingWav = false; ! 84: nWavOutputBytes = 0; ! 85: ! 86: /* Set frequency (11Khz, 22Khz or 44Khz) */ ! 87: nSampleFreq = ConfigureParams.Sound.nPlaybackFreq; ! 88: /* multiply by 4 for 16 bit stereo */ ! 89: nBytesPerSec = nSampleFreq * 4; ! 90: ! 91: /* Create our file */ ! 92: WavFileHndl = fopen(pszWavFileName, "wb"); ! 93: if (!WavFileHndl) ! 94: { ! 95: perror("WAVFormat_OpenFile"); ! 96: Log_AlertDlg(LOG_ERROR, "WAV recording: Failed to open file!"); ! 97: return false; ! 98: } ! 99: ! 100: /* Patch sample frequency in header structure */ ! 101: WavHeader[24] = (Uint8)nSampleFreq; ! 102: WavHeader[25] = (Uint8)(nSampleFreq >> 8); ! 103: WavHeader[26] = (Uint8)(nSampleFreq >> 16); ! 104: WavHeader[27] = (Uint8)(nSampleFreq >> 24); ! 105: ! 106: /* Patch bytes per second in header structure */ ! 107: WavHeader[28] = (Uint8)nBytesPerSec; ! 108: WavHeader[29] = (Uint8)(nBytesPerSec >> 8); ! 109: WavHeader[30] = (Uint8)(nBytesPerSec >> 16); ! 110: WavHeader[31] = (Uint8)(nBytesPerSec >> 24); ! 111: ! 112: /* Write header to file */ ! 113: if (fwrite(&WavHeader, sizeof(WavHeader), 1, WavFileHndl) == 1) ! 114: { ! 115: bRecordingWav = true; ! 116: Log_AlertDlg(LOG_INFO, "WAV sound data recording has been started."); ! 117: } ! 118: else ! 119: { ! 120: perror("WAVFormat_OpenFile"); ! 121: Log_AlertDlg(LOG_ERROR, "WAV recording: Failed to write header!"); ! 122: } ! 123: ! 124: /* Ok, or failed? */ ! 125: return bRecordingWav; ! 126: } ! 127: ! 128: ! 129: /** ! 130: * Write sizes to WAV header, then close the WAV file. ! 131: */ ! 132: void WAVFormat_CloseFile(void) ! 133: { ! 134: if (bRecordingWav) ! 135: { ! 136: Uint32 nWavFileBytes; ! 137: Uint32 nWavLEOutBytes; ! 138: ! 139: bRecordingWav = false; ! 140: ! 141: /* Update headers with sizes */ ! 142: nWavFileBytes = SDL_SwapLE32((12+24+8+nWavOutputBytes)-8); /* File length, less 8 bytes for 'RIFF' and length */ ! 143: fseek(WavFileHndl, 4, SEEK_SET); /* 'Total Length Of Package' element */ ! 144: /* Write total length of package in 'RIFF' chunk */ ! 145: if (fwrite(&nWavFileBytes, sizeof(Uint32), 1, WavFileHndl) != 1) ! 146: { ! 147: perror("WAVFormat_CloseFile"); ! 148: fclose(WavFileHndl); ! 149: WavFileHndl = NULL; ! 150: return; ! 151: } ! 152: ! 153: fseek(WavFileHndl, 12+24+4, SEEK_SET); /* 'Length' element */ ! 154: nWavLEOutBytes = SDL_SwapLE32(nWavOutputBytes); ! 155: /* Write length of data in 'DATA' chunk */ ! 156: if (fwrite(&nWavLEOutBytes, sizeof(Uint32), 1, WavFileHndl) != 1) ! 157: { ! 158: perror("WAVFormat_CloseFile"); ! 159: } ! 160: ! 161: /* Close file */ ! 162: fclose(WavFileHndl); ! 163: WavFileHndl = NULL; ! 164: ! 165: /* And inform user */ ! 166: Log_AlertDlg(LOG_INFO, "WAV Sound data recording has been stopped."); ! 167: } ! 168: } ! 169: ! 170: ! 171: /** ! 172: * Update WAV file with current samples ! 173: */ ! 174: void WAVFormat_Update(Sint16 pSamples[][2], int Index, int Length) ! 175: { ! 176: Sint16 sample[2]; ! 177: int i; ! 178: ! 179: if (bRecordingWav) ! 180: { ! 181: /* Output, better if did in two section if wrap */ ! 182: for(i = 0; i < Length; i++) ! 183: { ! 184: /* Convert sample to little endian */ ! 185: sample[0] = SDL_SwapLE16(pSamples[(Index+i)%MIXBUFFER_SIZE][0]); ! 186: sample[1] = SDL_SwapLE16(pSamples[(Index+i)%MIXBUFFER_SIZE][1]); ! 187: /* And store */ ! 188: if (fwrite(&sample, sizeof(sample), 1, WavFileHndl) != 1) ! 189: { ! 190: perror("WAVFormat_Update"); ! 191: WAVFormat_CloseFile(); ! 192: return; ! 193: } ! 194: } ! 195: ! 196: /* Add samples to wav file length counter */ ! 197: nWavOutputBytes += Length * 4; ! 198: } ! 199: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.