Rename some struct members
[openal-soft.git] / Alc / backends / wave.cpp
blob2475aa9fc56eca6645941fc2e6d94bef17df5612
1 /**
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
21 #include "config.h"
23 #include "backends/wave.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <memory.h>
28 #include <errno.h>
30 #include <chrono>
31 #include <thread>
32 #include <vector>
34 #include "alMain.h"
35 #include "alu.h"
36 #include "alconfig.h"
37 #include "compat.h"
40 namespace {
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 {
82 FILE *mFile{nullptr};
83 long mDataStart{-1};
85 al::vector<ALbyte> mBuffer;
87 std::atomic<ALenum> mKillNow{AL_TRUE};
88 std::thread mThread;
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)
118 if(self->mFile)
119 fclose(self->mFile);
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)};
135 ALint64 done{0};
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);
147 continue;
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);
159 ALuint i;
161 if(bytesize == 2)
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)};
185 (void)fs;
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);
192 break;
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};
204 start += s;
205 done -= device->Frequency*s.count();
209 return 0;
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;
218 if(!name)
219 name = waveDevice;
220 else if(strcmp(name, waveDevice) != 0)
221 return ALC_INVALID_VALUE;
223 #ifdef _WIN32
225 std::wstring wname = utf8_to_wstr(fname);
226 self->mFile = _wfopen(wname.c_str(), L"wb");
228 #else
229 self->mFile = fopen(fname, "wb");
230 #endif
231 if(!self->mFile)
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;
240 return ALC_NO_ERROR;
243 ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
245 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
246 ALuint channels=0, bits=0, chanmask=0;
247 int isbformat = 0;
248 size_t val;
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)
261 case DevFmtByte:
262 device->FmtType = DevFmtUByte;
263 break;
264 case DevFmtUShort:
265 device->FmtType = DevFmtShort;
266 break;
267 case DevFmtUInt:
268 device->FmtType = DevFmtInt;
269 break;
270 case DevFmtUByte:
271 case DevFmtShort:
272 case DevFmtInt:
273 case DevFmtFloat:
274 break;
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;
285 case DevFmtAmbi3D:
286 /* .amb output requires FuMa */
287 device->mAmbiLayout = AmbiLayout::FuMa;
288 device->mAmbiScale = AmbiNorm::FuMa;
289 isbformat = 1;
290 chanmask = 0;
291 break;
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);
326 (void)val;
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));
334 return ALC_FALSE;
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);
345 return ALC_TRUE;
348 ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
350 try {
351 self->mKillNow.store(AL_FALSE, std::memory_order_release);
352 self->mThread = std::thread(ALCwaveBackend_mixerProc, self);
353 return ALC_TRUE;
355 catch(std::exception& e) {
356 ERR("Failed to start mixing thread: %s\n", e.what());
358 catch(...) {
360 return ALC_FALSE;
363 void ALCwaveBackend_stop(ALCwaveBackend *self)
365 if(self->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !self->mThread.joinable())
366 return;
367 self->mThread.join();
369 long size{ftell(self->mFile)};
370 if(size > 0)
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
380 } // namespace
383 bool WaveBackendFactory::init()
384 { return true; }
386 bool WaveBackendFactory::querySupport(ALCbackend_Type type)
387 { return (type == ALCbackend_Playback); }
389 void WaveBackendFactory::probe(enum DevProbe type, std::string *outnames)
391 switch(type)
393 case ALL_DEVICE_PROBE:
394 /* Includes null char. */
395 outnames->append(waveDevice, sizeof(waveDevice));
396 break;
397 case CAPTURE_DEVICE_PROBE:
398 break;
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);
412 return nullptr;
415 BackendFactory &WaveBackendFactory::getFactory()
417 static WaveBackendFactory factory{};
418 return factory;