1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2 /*
3     bqaudiostream
4 
5     A small library wrapping various audio file read/write
6     implementations in C++.
7 
8     Copyright 2007-2015 Particular Programs Ltd.
9 
10     Permission is hereby granted, free of charge, to any person
11     obtaining a copy of this software and associated documentation
12     files (the "Software"), to deal in the Software without
13     restriction, including without limitation the rights to use, copy,
14     modify, merge, publish, distribute, sublicense, and/or sell copies
15     of the Software, and to permit persons to whom the Software is
16     furnished to do so, subject to the following conditions:
17 
18     The above copyright notice and this permission notice shall be
19     included in all copies or substantial portions of the Software.
20 
21     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
25     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
26     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 
29     Except as contained in this notice, the names of Chris Cannam and
30     Particular Programs Ltd shall not be used in advertising or
31     otherwise to promote the sale, use or other dealings in this
32     Software without prior written authorization.
33 */
34 
35 #include "SimpleWavFileWriteStream.h"
36 
37 #if ! (defined(HAVE_LIBSNDFILE) || defined(HAVE_SNDFILE))
38 
39 #include "Exceptions.h"
40 #include <iostream>
41 
42 using namespace std;
43 
44 namespace breakfastquay
45 {
46 
getOpusExtensions()47 static vector<string> extensions() {
48     vector<string> ee;
49     ee.push_back("wav");
50     return ee;
51 }
52 
53 static
54 AudioWriteStreamBuilder<SimpleWavFileWriteStream>
55 simplewavbuilder(
56     string("http://breakfastquay.com/rdf/turbot/audiostream/SimpleWavFileWriteStream"),
57     extensions()
58     );
59 
60 SimpleWavFileWriteStream::SimpleWavFileWriteStream(Target target) :
61     AudioWriteStream(target),
62     m_bitDepth(24),
63     m_file(0)
D()64 {
65     m_file = new ofstream(getPath().c_str(), ios::out | std::ios::binary);
66 
67     if (!*m_file) {
68         delete m_file;
69         m_file = 0;
70         cerr << "SimpleWavFileWriteStream: Failed to open output file for writing" << endl;
71         m_error = string("Failed to open audio file '") +
72             getPath() + "' for writing";
73         throw FailedToWriteFile(getPath());
74     }
75 
76     writeFormatChunk();
77 }
78 
79 SimpleWavFileWriteStream::~SimpleWavFileWriteStream()
80 {
81     if (!m_file) {
82         return;
83     }
84 
85     m_file->seekp(0, ios::end);
86     unsigned int totalSize = m_file->tellp();
87 
88     // seek to first length position
89     m_file->seekp(4, ios::beg);
90 
91     // write complete file size minus 8 bytes to here
92     putBytes(int2le(totalSize - 8, 4));
93 
94     // reseek from start forward 40
95     m_file->seekp(40, ios::beg);
96 
97     // write the data chunk size to end
98     putBytes(int2le(totalSize - 44, 4));
99 
100     m_file->close();
101 
102     delete m_file;
103     m_file = 0;
104 }
105 
106 void
107 SimpleWavFileWriteStream::putBytes(string s)
108 {
109     if (!m_file) return;
110     for (unsigned int i = 0; i < s.length(); i++) {
111         *m_file << (unsigned char)s[i];
112     }
113 }
114 
115 void
getFrames(size_t count,float * frames)116 SimpleWavFileWriteStream::putBytes(const unsigned char *buffer, size_t n)
117 {
118     if (!m_file) return;
119     m_file->write((const char *)buffer, n);
120 }
121 
122 string
123 SimpleWavFileWriteStream::int2le(unsigned int value, unsigned int length)
124 {
125     string r;
126 
127     do {
128         r += (unsigned char)((long)((value >> (8 * r.length())) & 0xff));
129     } while (r.length() < length);
130 
131     return r;
132 }
133 
134 void
135 SimpleWavFileWriteStream::writeFormatChunk()
136 {
137     if (!m_file) return;
138 
139     string outString;
140 
141     outString += "RIFF";
142     outString += "0000";
143     outString += "WAVE";
144     outString += "fmt ";
145 
146     // length
147     outString += int2le(0x10, 4);
148 
149     // 1 for PCM, 3 for float
150     outString += int2le(0x01, 2);
151 
152     // channels
153     outString += int2le(getChannelCount(), 2);
154 
155     // sample rate
156     outString += int2le(getSampleRate(), 4);
157 
158     // bytes per second
159     outString += int2le((m_bitDepth / 8) * getChannelCount() * getSampleRate(), 4);
160 
161     // bytes per frame
162     outString += int2le((m_bitDepth / 8) * getChannelCount(), 2);
163 
164     // bits per sample
165     outString += int2le(m_bitDepth, 2);
166 
167     outString += "data";
168     outString += "0000";
169 
170     putBytes(outString);
171 }
172 
173 void
174 SimpleWavFileWriteStream::putInterleavedFrames(size_t count, float *frames)
175 {
176     if (count == 0) return;
177 
178     for (size_t i = 0; i < count; ++i) {
179         for (size_t c = 0; c < getChannelCount(); ++c) {
180 
181             double f = frames[i * getChannelCount() + c];
182             unsigned int u = 0;
183             unsigned char ubuf[4];
184             if (f < -1.0) f = -1.0;
185             if (f > 1.0) f = 1.0;
186 
187             switch (m_bitDepth) {
188 
189             case 24:
190                 f = f * 2147483647.0;
191                 u = (unsigned int)(int(f));
192                 u >>= 8;
193                 ubuf[0] = (u & 0xff);
194                 u >>= 8;
195                 ubuf[1] = (u & 0xff);
196                 u >>= 8;
197                 ubuf[2] = (u & 0xff);
198                 break;
199 
200             default:
201                 ubuf[0] = ubuf[1] = ubuf[2] = ubuf[3] = '\0';
202                 break;
203             }
204 
205             putBytes(ubuf, m_bitDepth / 8);
206         }
207     }
208 }
209 
~OpusReadStream()210 }
211 
212 #endif
213