2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
23 #include "backends/wave.h"
42 using std::chrono::seconds
;
43 using std::chrono::milliseconds
;
44 using std::chrono::nanoseconds
;
46 constexpr ALCchar waveDevice
[] = "Wave File Writer";
48 constexpr ALubyte SUBTYPE_PCM
[]{
49 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
50 0x00, 0x38, 0x9b, 0x71
52 constexpr ALubyte SUBTYPE_FLOAT
[]{
53 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
54 0x00, 0x38, 0x9b, 0x71
57 constexpr ALubyte SUBTYPE_BFORMAT_PCM
[]{
58 0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
59 0xca, 0x00, 0x00, 0x00
62 constexpr ALubyte SUBTYPE_BFORMAT_FLOAT
[]{
63 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
64 0xca, 0x00, 0x00, 0x00
67 void fwrite16le(ALushort val
, FILE *f
)
69 ALubyte data
[2]{ static_cast<ALubyte
>(val
&0xff), static_cast<ALubyte
>((val
>>8)&0xff) };
70 fwrite(data
, 1, 2, f
);
73 void fwrite32le(ALuint val
, FILE *f
)
75 ALubyte data
[4]{ static_cast<ALubyte
>(val
&0xff), static_cast<ALubyte
>((val
>>8)&0xff),
76 static_cast<ALubyte
>((val
>>16)&0xff), static_cast<ALubyte
>((val
>>24)&0xff) };
77 fwrite(data
, 1, 4, f
);
81 struct ALCwaveBackend final
: public ALCbackend
{
85 al::vector
<ALbyte
> mBuffer
;
87 std::atomic
<ALenum
> mKillNow
{AL_TRUE
};
91 int ALCwaveBackend_mixerProc(ALCwaveBackend
*self
);
93 void ALCwaveBackend_Construct(ALCwaveBackend
*self
, ALCdevice
*device
);
94 void ALCwaveBackend_Destruct(ALCwaveBackend
*self
);
95 ALCenum
ALCwaveBackend_open(ALCwaveBackend
*self
, const ALCchar
*name
);
96 ALCboolean
ALCwaveBackend_reset(ALCwaveBackend
*self
);
97 ALCboolean
ALCwaveBackend_start(ALCwaveBackend
*self
);
98 void ALCwaveBackend_stop(ALCwaveBackend
*self
);
99 DECLARE_FORWARD2(ALCwaveBackend
, ALCbackend
, ALCenum
, captureSamples
, void*, ALCuint
)
100 DECLARE_FORWARD(ALCwaveBackend
, ALCbackend
, ALCuint
, availableSamples
)
101 DECLARE_FORWARD(ALCwaveBackend
, ALCbackend
, ClockLatency
, getClockLatency
)
102 DECLARE_FORWARD(ALCwaveBackend
, ALCbackend
, void, lock
)
103 DECLARE_FORWARD(ALCwaveBackend
, ALCbackend
, void, unlock
)
104 DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend
)
106 DEFINE_ALCBACKEND_VTABLE(ALCwaveBackend
);
109 void ALCwaveBackend_Construct(ALCwaveBackend
*self
, ALCdevice
*device
)
111 new (self
) ALCwaveBackend
{};
112 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
113 SET_VTABLE2(ALCwaveBackend
, ALCbackend
, self
);
116 void ALCwaveBackend_Destruct(ALCwaveBackend
*self
)
120 self
->mFile
= nullptr;
122 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
123 self
->~ALCwaveBackend();
126 int ALCwaveBackend_mixerProc(ALCwaveBackend
*self
)
128 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
129 const milliseconds restTime
{device
->UpdateSize
*1000/device
->Frequency
/ 2};
131 althrd_setname(MIXER_THREAD_NAME
);
133 ALsizei frameSize
{FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
)};
136 auto start
= std::chrono::steady_clock::now();
137 while(!self
->mKillNow
.load(std::memory_order_acquire
) &&
138 ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
140 auto now
= std::chrono::steady_clock::now();
142 /* This converts from nanoseconds to nanosamples, then to samples. */
143 ALint64 avail
{std::chrono::duration_cast
<seconds
>((now
-start
) * device
->Frequency
).count()};
144 if(avail
-done
< device
->UpdateSize
)
146 std::this_thread::sleep_for(restTime
);
149 while(avail
-done
>= device
->UpdateSize
)
151 ALCwaveBackend_lock(self
);
152 aluMixData(device
, self
->mBuffer
.data(), device
->UpdateSize
);
153 ALCwaveBackend_unlock(self
);
154 done
+= device
->UpdateSize
;
156 if(!IS_LITTLE_ENDIAN
)
158 ALuint bytesize
= BytesFromDevFmt(device
->FmtType
);
163 ALushort
*samples
= reinterpret_cast<ALushort
*>(self
->mBuffer
.data());
164 ALuint len
= self
->mBuffer
.size() / 2;
165 for(i
= 0;i
< len
;i
++)
167 ALushort samp
= samples
[i
];
168 samples
[i
] = (samp
>>8) | (samp
<<8);
171 else if(bytesize
== 4)
173 ALuint
*samples
= reinterpret_cast<ALuint
*>(self
->mBuffer
.data());
174 ALuint len
= self
->mBuffer
.size() / 4;
175 for(i
= 0;i
< len
;i
++)
177 ALuint samp
= samples
[i
];
178 samples
[i
] = (samp
>>24) | ((samp
>>8)&0x0000ff00) |
179 ((samp
<<8)&0x00ff0000) | (samp
<<24);
184 size_t fs
{fwrite(self
->mBuffer
.data(), frameSize
, device
->UpdateSize
, self
->mFile
)};
186 if(ferror(self
->mFile
))
188 ERR("Error writing to file\n");
189 ALCwaveBackend_lock(self
);
190 aluHandleDisconnect(device
, "Failed to write playback samples");
191 ALCwaveBackend_unlock(self
);
196 /* For every completed second, increment the start time and reduce the
197 * samples done. This prevents the difference between the start time
198 * and current time from growing too large, while maintaining the
199 * correct number of samples to render.
201 if(done
>= device
->Frequency
)
203 seconds s
{done
/device
->Frequency
};
205 done
-= device
->Frequency
*s
.count();
213 ALCenum
ALCwaveBackend_open(ALCwaveBackend
*self
, const ALCchar
*name
)
215 const char *fname
{GetConfigValue(nullptr, "wave", "file", "")};
216 if(!fname
[0]) return ALC_INVALID_VALUE
;
220 else if(strcmp(name
, waveDevice
) != 0)
221 return ALC_INVALID_VALUE
;
225 std::wstring wname
= utf8_to_wstr(fname
);
226 self
->mFile
= _wfopen(wname
.c_str(), L
"wb");
229 self
->mFile
= fopen(fname
, "wb");
233 ERR("Could not open file '%s': %s\n", fname
, strerror(errno
));
234 return ALC_INVALID_VALUE
;
237 ALCdevice
*device
{STATIC_CAST(ALCbackend
, self
)->mDevice
};
238 device
->DeviceName
= name
;
243 ALCboolean
ALCwaveBackend_reset(ALCwaveBackend
*self
)
245 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
246 ALuint channels
=0, bits
=0, chanmask
=0;
250 fseek(self
->mFile
, 0, SEEK_SET
);
251 clearerr(self
->mFile
);
253 if(GetConfigValueBool(nullptr, "wave", "bformat", 0))
255 device
->FmtChans
= DevFmtAmbi3D
;
256 device
->mAmbiOrder
= 1;
259 switch(device
->FmtType
)
262 device
->FmtType
= DevFmtUByte
;
265 device
->FmtType
= DevFmtShort
;
268 device
->FmtType
= DevFmtInt
;
276 switch(device
->FmtChans
)
278 case DevFmtMono
: chanmask
= 0x04; break;
279 case DevFmtStereo
: chanmask
= 0x01 | 0x02; break;
280 case DevFmtQuad
: chanmask
= 0x01 | 0x02 | 0x10 | 0x20; break;
281 case DevFmtX51
: chanmask
= 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
282 case DevFmtX51Rear
: chanmask
= 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break;
283 case DevFmtX61
: chanmask
= 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
284 case DevFmtX71
: chanmask
= 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
286 /* .amb output requires FuMa */
287 device
->mAmbiLayout
= AmbiLayout::FuMa
;
288 device
->mAmbiScale
= AmbiNorm::FuMa
;
293 bits
= BytesFromDevFmt(device
->FmtType
) * 8;
294 channels
= ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
);
296 fputs("RIFF", self
->mFile
);
297 fwrite32le(0xFFFFFFFF, self
->mFile
); // 'RIFF' header len; filled in at close
299 fputs("WAVE", self
->mFile
);
301 fputs("fmt ", self
->mFile
);
302 fwrite32le(40, self
->mFile
); // 'fmt ' header len; 40 bytes for EXTENSIBLE
304 // 16-bit val, format type id (extensible: 0xFFFE)
305 fwrite16le(0xFFFE, self
->mFile
);
306 // 16-bit val, channel count
307 fwrite16le(channels
, self
->mFile
);
308 // 32-bit val, frequency
309 fwrite32le(device
->Frequency
, self
->mFile
);
310 // 32-bit val, bytes per second
311 fwrite32le(device
->Frequency
* channels
* bits
/ 8, self
->mFile
);
312 // 16-bit val, frame size
313 fwrite16le(channels
* bits
/ 8, self
->mFile
);
314 // 16-bit val, bits per sample
315 fwrite16le(bits
, self
->mFile
);
316 // 16-bit val, extra byte count
317 fwrite16le(22, self
->mFile
);
318 // 16-bit val, valid bits per sample
319 fwrite16le(bits
, self
->mFile
);
320 // 32-bit val, channel mask
321 fwrite32le(chanmask
, self
->mFile
);
322 // 16 byte GUID, sub-type format
323 val
= fwrite((device
->FmtType
== DevFmtFloat
) ?
324 (isbformat
? SUBTYPE_BFORMAT_FLOAT
: SUBTYPE_FLOAT
) :
325 (isbformat
? SUBTYPE_BFORMAT_PCM
: SUBTYPE_PCM
), 1, 16, self
->mFile
);
328 fputs("data", self
->mFile
);
329 fwrite32le(0xFFFFFFFF, self
->mFile
); // 'data' header len; filled in at close
331 if(ferror(self
->mFile
))
333 ERR("Error writing header: %s\n", strerror(errno
));
336 self
->mDataStart
= ftell(self
->mFile
);
338 SetDefaultWFXChannelOrder(device
);
340 ALuint bufsize
{FrameSizeFromDevFmt(
341 device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
342 ) * device
->UpdateSize
};
343 self
->mBuffer
.resize(bufsize
);
348 ALCboolean
ALCwaveBackend_start(ALCwaveBackend
*self
)
351 self
->mKillNow
.store(AL_FALSE
, std::memory_order_release
);
352 self
->mThread
= std::thread(ALCwaveBackend_mixerProc
, self
);
355 catch(std::exception
& e
) {
356 ERR("Failed to start mixing thread: %s\n", e
.what());
363 void ALCwaveBackend_stop(ALCwaveBackend
*self
)
365 if(self
->mKillNow
.exchange(AL_TRUE
, std::memory_order_acq_rel
) || !self
->mThread
.joinable())
367 self
->mThread
.join();
369 long size
{ftell(self
->mFile
)};
372 long dataLen
{size
- self
->mDataStart
};
373 if(fseek(self
->mFile
, self
->mDataStart
-4, SEEK_SET
) == 0)
374 fwrite32le(dataLen
, self
->mFile
); // 'data' header len
375 if(fseek(self
->mFile
, 4, SEEK_SET
) == 0)
376 fwrite32le(size
-8, self
->mFile
); // 'WAVE' header len
383 bool WaveBackendFactory::init()
386 bool WaveBackendFactory::querySupport(ALCbackend_Type type
)
387 { return (type
== ALCbackend_Playback
); }
389 void WaveBackendFactory::probe(enum DevProbe type
, std::string
*outnames
)
393 case ALL_DEVICE_PROBE
:
394 /* Includes null char. */
395 outnames
->append(waveDevice
, sizeof(waveDevice
));
397 case CAPTURE_DEVICE_PROBE
:
402 ALCbackend
*WaveBackendFactory::createBackend(ALCdevice
*device
, ALCbackend_Type type
)
404 if(type
== ALCbackend_Playback
)
406 ALCwaveBackend
*backend
;
407 NEW_OBJ(backend
, ALCwaveBackend
)(device
);
408 if(!backend
) return nullptr;
409 return STATIC_CAST(ALCbackend
, backend
);
415 BackendFactory
&WaveBackendFactory::getFactory()
417 static WaveBackendFactory factory
{};