-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsndsave.c
191 lines (153 loc) · 5.7 KB
/
sndsave.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
* sndsave.c - reading and writing sound to files
*
* Copyright (C) 1995-1998 David Firth
* Copyright (C) 1998-2005 Atari800 development team (see DOC/CREDITS)
*
* This file is part of the Atari800 emulator project which emulates
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
*
* Atari800 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Atari800 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Atari800; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include "pokeysnd.h"
#include "sndsave.h"
/* sndoutput is just the file pointer for the current sound file */
static FILE *sndoutput = NULL;
static ULONG byteswritten;
/* write 32-bit word as little endian */
static void write32(long x)
{
fputc(x & 0xff, sndoutput);
fputc((x >> 8) & 0xff, sndoutput);
fputc((x >> 16) & 0xff, sndoutput);
fputc((x >> 24) & 0xff, sndoutput);
}
/* SndSave_IsSoundFileOpen simply returns true if the sound file is currently open and able to receive writes
RETURNS: TRUE is file is open, FALSE if it is not */
int SndSave_IsSoundFileOpen(void)
{
return sndoutput != NULL;
}
/* SndSave_CloseSoundFile should be called when the program is exiting, or when all data required has been
written to the file. SndSave_CloseSoundFile will also be called automatically when a call is made to
SndSave_OpenSoundFile, or an error is made in SndSave_WriteToSoundFile. Note that CloseSoundFile has to back track
to the header written out in SndSave_OpenSoundFile and update it with the length of samples written
RETURNS: TRUE if file closed with no problems, FALSE if failure during close */
int SndSave_CloseSoundFile(void)
{
int bSuccess = TRUE;
char aligned = 0;
if (sndoutput != NULL) {
/* A RIFF file's chunks must be word-aligned. So let's align. */
if (byteswritten & 1) {
if (putc(0, sndoutput) == EOF)
bSuccess = FALSE;
else
aligned = 1;
}
if (bSuccess) {
/* Sound file is finished, so modify header and close it. */
if (fseek(sndoutput, 4, SEEK_SET) != 0) /* Seek past RIFF */
bSuccess = FALSE;
else {
/* RIFF header's size field must equal the size of all chunks
* with alignment, so the alignment byte is added.
*/
write32(byteswritten + 36 + aligned);
if (fseek(sndoutput, 40, SEEK_SET) != 0)
bSuccess = FALSE;
else {
/* But in the "data" chunk size field, the alignment byte
* should be ignored. */
write32(byteswritten);
}
}
}
fclose(sndoutput);
sndoutput = NULL;
}
return bSuccess;
}
/* SndSave_OpenSoundFile will start a new sound file and write out the header. If an existing sound file is
already open it will be closed first, and the new file opened in it's place
RETURNS: TRUE if file opened with no problems, FALSE if failure during open */
int SndSave_OpenSoundFile(const char *szFileName)
{
SndSave_CloseSoundFile();
sndoutput = fopen(szFileName, "wb");
if (sndoutput == NULL)
return FALSE;
/*
The RIFF header:
Offset Length Contents
0 4 bytes 'RIFF'
4 4 bytes <file length - 8>
8 4 bytes 'WAVE'
The fmt chunk:
12 4 bytes 'fmt '
16 4 bytes 0x00000010 // Length of the fmt data (16 bytes)
20 2 bytes 0x0001 // Format tag: 1 = PCM
22 2 bytes <channels> // Channels: 1 = mono, 2 = stereo
24 4 bytes <sample rate> // Samples per second: e.g., 44100
28 4 bytes <bytes/second> // sample rate * block align
32 2 bytes <block align> // channels * bits/sample / 8
34 2 bytes <bits/sample> // 8 or 16
The data chunk:
36 4 bytes 'data'
40 4 bytes <length of the data block>
44 bytes <sample data>
All chunks must be word-aligned.
Good description of WAVE format: http://www.sonicspot.com/guide/wavefiles.html
*/
if (fwrite("RIFF\0\0\0\0WAVEfmt \x10\0\0\0\1\0", 1, 22, sndoutput) != 22) {
fclose(sndoutput);
sndoutput = NULL;
return FALSE;
}
fputc(POKEYSND_num_pokeys, sndoutput);
fputc(0, sndoutput);
write32(POKEYSND_playback_freq);
write32(POKEYSND_playback_freq * (POKEYSND_snd_flags & POKEYSND_BIT16 ? POKEYSND_num_pokeys << 1 : POKEYSND_num_pokeys));
fputc(POKEYSND_snd_flags & POKEYSND_BIT16 ? POKEYSND_num_pokeys << 1 : POKEYSND_num_pokeys, sndoutput);
fputc(0, sndoutput);
fputc(POKEYSND_snd_flags & POKEYSND_BIT16? 16: 8, sndoutput);
if (fwrite("\0data\0\0\0\0", 1, 9, sndoutput) != 9) {
fclose(sndoutput);
sndoutput = NULL;
return FALSE;
}
byteswritten = 0;
return TRUE;
}
/* SndSave_WriteToSoundFile will dump PCM data to the WAV file. The best way to do this for Atari800 is
probably to call it directly after POKEYSND_Process(buffer, size) with the same values (buffer, size)
RETURNS: the number of bytes written to the file (should be equivalent to the input uiSize parm) */
int SndSave_WriteToSoundFile(const unsigned char *ucBuffer, unsigned int uiSize)
{
/* XXX FIXME: doesn't work with big-endian architectures */
if (sndoutput && ucBuffer && uiSize) {
int result;
if (POKEYSND_snd_flags & POKEYSND_BIT16)
uiSize <<= 1;
result = fwrite(ucBuffer, 1, uiSize, sndoutput);
byteswritten += result;
if (result != uiSize) {
SndSave_CloseSoundFile();
}
return result;
}
return 0;
}