|
|
1.1 root 1: /*
1.1.1.4 root 2: Hatari - wavFormat.c
3:
1.1.1.13 root 4: This file is distributed under the GNU General Public License, version 2
5: or at your option any later version. Read the file gpl.txt for details.
1.1 root 6:
7: WAV File output
8:
1.1.1.6 root 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.
1.1 root 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: */
1.1.1.12 root 36: const char WAVFormat_fileid[] = "Hatari wavFormat.c : " __DATE__ " " __TIME__;
1.1 root 37:
1.1.1.3 root 38: #include <SDL_endian.h>
39:
1.1 root 40: #include "main.h"
41: #include "audio.h"
1.1.1.6 root 42: #include "configuration.h"
1.1 root 43: #include "file.h"
1.1.1.6 root 44: #include "log.h"
1.1 root 45: #include "sound.h"
1.1.1.5 root 46: #include "wavFormat.h"
1.1.1.2 root 47:
1.1 root 48:
1.1.1.3 root 49: static FILE *WavFileHndl;
50: static int nWavOutputBytes; /* Number of samples bytes saved */
1.1.1.12 root 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: };
1.1.1.3 root 73:
1.1 root 74:
1.1.1.9 root 75: /**
1.1.1.12 root 76: * Open WAV output file and write header.
1.1.1.9 root 77: */
1.1.1.10 root 78: bool WAVFormat_OpenFile(char *pszWavFileName)
1.1 root 79: {
1.1.1.12 root 80:
81: Uint32 nSampleFreq, nBytesPerSec;
82:
83: bRecordingWav = false;
84: nWavOutputBytes = 0;
1.1.1.6 root 85:
86: /* Set frequency (11Khz, 22Khz or 44Khz) */
1.1.1.12 root 87: nSampleFreq = ConfigureParams.Sound.nPlaybackFreq;
88: /* multiply by 4 for 16 bit stereo */
89: nBytesPerSec = nSampleFreq * 4;
1.1.1.6 root 90:
91: /* Create our file */
92: WavFileHndl = fopen(pszWavFileName, "wb");
1.1.1.12 root 93: if (!WavFileHndl)
1.1.1.6 root 94: {
1.1.1.12 root 95: perror("WAVFormat_OpenFile");
96: Log_AlertDlg(LOG_ERROR, "WAV recording: Failed to open file!");
97: return false;
98: }
1.1.1.6 root 99:
1.1.1.12 root 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);
1.1.1.6 root 111:
1.1.1.12 root 112: /* Write header to file */
113: if (fwrite(&WavHeader, sizeof(WavHeader), 1, WavFileHndl) == 1)
114: {
115: bRecordingWav = true;
1.1.1.6 root 116: Log_AlertDlg(LOG_INFO, "WAV sound data recording has been started.");
117: }
118: else
1.1.1.12 root 119: {
120: perror("WAVFormat_OpenFile");
121: Log_AlertDlg(LOG_ERROR, "WAV recording: Failed to write header!");
122: }
1.1 root 123:
1.1.1.6 root 124: /* Ok, or failed? */
125: return bRecordingWav;
1.1 root 126: }
127:
1.1.1.3 root 128:
1.1.1.9 root 129: /**
1.1.1.12 root 130: * Write sizes to WAV header, then close the WAV file.
1.1.1.9 root 131: */
132: void WAVFormat_CloseFile(void)
1.1 root 133: {
1.1.1.6 root 134: if (bRecordingWav)
135: {
136: Uint32 nWavFileBytes;
137: Uint32 nWavLEOutBytes;
138:
1.1.1.12 root 139: bRecordingWav = false;
140:
1.1.1.6 root 141: /* Update headers with sizes */
142: nWavFileBytes = SDL_SwapLE32((12+24+8+nWavOutputBytes)-8); /* File length, less 8 bytes for 'RIFF' and length */
1.1.1.14! root 143: /* Seek to 'Total Length Of Package' element and
! 144: * write total length of package in 'RIFF' chunk */
! 145: if (fseek(WavFileHndl, 4, SEEK_SET) != 0 ||
! 146: fwrite(&nWavFileBytes, sizeof(Uint32), 1, WavFileHndl) != 1)
1.1.1.12 root 147: {
148: perror("WAVFormat_CloseFile");
149: fclose(WavFileHndl);
150: WavFileHndl = NULL;
151: return;
152: }
1.1.1.6 root 153:
154: nWavLEOutBytes = SDL_SwapLE32(nWavOutputBytes);
1.1.1.14! root 155: /* Seek to 'Length' element and write length of data in 'DATA' chunk */
! 156: if (fseek(WavFileHndl, 12+24+4, SEEK_SET) != 0
! 157: || fwrite(&nWavLEOutBytes, sizeof(Uint32), 1, WavFileHndl) != 1)
1.1.1.12 root 158: {
159: perror("WAVFormat_CloseFile");
160: }
1.1.1.6 root 161:
162: /* Close file */
163: fclose(WavFileHndl);
164: WavFileHndl = NULL;
165:
166: /* And inform user */
167: Log_AlertDlg(LOG_INFO, "WAV Sound data recording has been stopped.");
168: }
1.1 root 169: }
170:
1.1.1.3 root 171:
1.1.1.9 root 172: /**
1.1.1.12 root 173: * Update WAV file with current samples
1.1.1.9 root 174: */
1.1.1.10 root 175: void WAVFormat_Update(Sint16 pSamples[][2], int Index, int Length)
1.1 root 176: {
1.1.1.10 root 177: Sint16 sample[2];
1.1.1.6 root 178: int i;
1.1.1.3 root 179:
1.1.1.6 root 180: if (bRecordingWav)
181: {
182: /* Output, better if did in two section if wrap */
183: for(i = 0; i < Length; i++)
184: {
1.1.1.10 root 185: /* Convert sample to little endian */
186: sample[0] = SDL_SwapLE16(pSamples[(Index+i)%MIXBUFFER_SIZE][0]);
187: sample[1] = SDL_SwapLE16(pSamples[(Index+i)%MIXBUFFER_SIZE][1]);
1.1.1.6 root 188: /* And store */
1.1.1.12 root 189: if (fwrite(&sample, sizeof(sample), 1, WavFileHndl) != 1)
190: {
191: perror("WAVFormat_Update");
192: WAVFormat_CloseFile();
193: return;
194: }
1.1.1.6 root 195: }
196:
1.1.1.12 root 197: /* Add samples to wav file length counter */
1.1.1.10 root 198: nWavOutputBytes += Length * 4;
1.1.1.6 root 199: }
1.1 root 200: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.