@babblevoice/projectrtp 2.5.34 → 2.5.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +2 -0
- package/Dockerfile +5 -7
- package/Dockerfile.debian +42 -0
- package/README.md +18 -1
- package/asan.options +2 -0
- package/binding.gyp +30 -3
- package/index.js +6 -1
- package/jsconfig.json +1 -1
- package/lib/server.js +6 -4
- package/package.json +6 -6
- package/src/globals.h +3 -0
- package/src/projectrtpbuffer.cpp +3 -0
- package/src/projectrtpchannel.cpp +176 -181
- package/src/projectrtpchannel.h +5 -6
- package/src/projectrtpchannelmux.cpp +45 -46
- package/src/projectrtpchannelmux.h +2 -4
- package/src/projectrtpchannelrecorder.h +4 -4
- package/src/projectrtpcodecx.cpp +36 -56
- package/src/projectrtpnodemain.cpp +3 -0
- package/src/projectrtppacket.cpp +3 -0
- package/src/projectrtprawsound.cpp +30 -11
- package/src/projectrtprawsound.h +3 -1
- package/src/projectrtpsoundfile.cpp +260 -182
- package/src/projectrtpsoundfile.h +14 -3
- package/src/projectrtpsoundsoup.cpp +2 -0
- package/src/projectrtpstun.cpp +3 -0
- package/stress/echorecord.scenario.js +4 -1
- package/stress/index.js +4 -1
- package/stress/playbackrecordtoofast.scenario.js +106 -0
- package/stress/playbackthenmix.scenario.js +70 -0
- package/stress/utils.js +27 -3
- package/test/interface/projectrtpdtmf.js +41 -12
- package/test/interface/projectrtpmix.js +78 -0
- package/test/interface/projectrtprecord.js +1 -1
- package/test/interface/projectrtpsound.js +121 -0
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
|
|
2
|
+
/* needed to build on ubuntu */
|
|
3
|
+
#include <utility>
|
|
4
|
+
|
|
2
5
|
#include <iostream>
|
|
6
|
+
#include <algorithm>
|
|
3
7
|
|
|
4
8
|
#include "projectrtpsoundfile.h"
|
|
5
9
|
#include "globals.h"
|
|
6
10
|
|
|
11
|
+
|
|
12
|
+
constexpr size_t AIOCBSINGLECHANBUFFNUMSAMPLES = L1616PAYLOADSAMPLES;
|
|
13
|
+
constexpr size_t AIOCBBUFFERNUMSAMPLES = ( AIOCBSINGLECHANBUFFNUMSAMPLES * MAXNUMBEROFCHANNELS );
|
|
14
|
+
|
|
7
15
|
/*
|
|
8
16
|
soundfile
|
|
9
17
|
*/
|
|
@@ -13,13 +21,8 @@ soundfile::soundfile( int fromfile ) :
|
|
|
13
21
|
ourwavheader(),
|
|
14
22
|
currentcbindex( 0 ),
|
|
15
23
|
cbwavheader(),
|
|
16
|
-
buffer(
|
|
17
|
-
|
|
18
|
-
/*
|
|
19
|
-
Soundfile blindly reads the format and passes to the codec - so it must be in a format we support - or there will be silence.
|
|
20
|
-
Our macro player (to be written) will choose the most appropriate file to play based on the codec of the channel.
|
|
21
|
-
*/
|
|
22
|
-
this->buffer = new uint8_t[ L16WIDEBANDBYTES * MAXNUMBEROFCHANNELS * SOUNDFILENUMBUFFERS * 2 /* 16 bit */ ];
|
|
24
|
+
buffer(),
|
|
25
|
+
filelock( true /* initialised as locked */ ) {
|
|
23
26
|
|
|
24
27
|
/* As it is asynchronous - we read wav header + ahead */
|
|
25
28
|
memset( &this->cbwavheader, 0, sizeof( aiocb ) );
|
|
@@ -30,29 +33,33 @@ soundfile::soundfile( int fromfile ) :
|
|
|
30
33
|
|
|
31
34
|
off_t fileoffset = sizeof( wavheader );
|
|
32
35
|
for( auto i = 0; i < SOUNDFILENUMBUFFERS; i++ ) {
|
|
33
|
-
|
|
36
|
+
soundbuffer &thisbuffer = this->buffer[ i ];
|
|
37
|
+
aiocb &thisblock = this->cbwavblock[ i ];
|
|
38
|
+
|
|
39
|
+
thisbuffer.reserve( AIOCBBUFFERNUMSAMPLES );
|
|
40
|
+
|
|
41
|
+
memset( &thisblock, 0, sizeof( aiocb ) );
|
|
34
42
|
this->cbwavblock[ i ].aio_fildes = this->file;
|
|
35
43
|
|
|
36
44
|
/* These 2 values are modified depending on format and num channels */
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
fileoffset +=
|
|
45
|
+
thisblock.aio_nbytes = AIOCBBUFFERNUMSAMPLES * sizeof( uint16_t );
|
|
46
|
+
thisblock.aio_offset = fileoffset;
|
|
47
|
+
fileoffset += thisblock.aio_nbytes;
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
this->cbwavblock[ i ].aio_buf = this->buffer + ( i * L16WIDEBANDBYTES * MAXNUMBEROFCHANNELS * 2 );
|
|
49
|
+
thisblock.aio_buf = thisbuffer.data();
|
|
43
50
|
}
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
soundfile::~soundfile() {
|
|
54
|
+
|
|
55
|
+
SpinLockGuard guard( this->filelock );
|
|
56
|
+
|
|
47
57
|
if ( -1 != this->file ) {
|
|
48
58
|
/* Is there a better way? Not waiting for the cancel can can cause the async
|
|
49
59
|
opperation to write to our buffer which is much worse */
|
|
50
60
|
while( AIO_NOTCANCELED == aio_cancel( this->file, NULL ) );
|
|
51
61
|
close( this->file );
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if( nullptr != this->buffer ) {
|
|
55
|
-
delete[] this->buffer;
|
|
62
|
+
this->file = -1;
|
|
56
63
|
}
|
|
57
64
|
}
|
|
58
65
|
|
|
@@ -98,7 +105,8 @@ perhaps monitor (std::cerr).
|
|
|
98
105
|
*/
|
|
99
106
|
soundfilereader::soundfilereader( std::string &url ) :
|
|
100
107
|
soundfile( open( url.c_str(), O_RDONLY | O_NONBLOCK, 0 ) ),
|
|
101
|
-
|
|
108
|
+
bytecount( L16WIDEBANDBYTES ),
|
|
109
|
+
samplecount( L1616PAYLOADSAMPLES ),
|
|
102
110
|
badheader( false ),
|
|
103
111
|
headerread( false ),
|
|
104
112
|
bodyread( false ),
|
|
@@ -107,6 +115,7 @@ soundfilereader::soundfilereader( std::string &url ) :
|
|
|
107
115
|
|
|
108
116
|
if ( -1 == this->file ) {
|
|
109
117
|
/* Not much more we can do */
|
|
118
|
+
releasespinlock( this->filelock );
|
|
110
119
|
return;
|
|
111
120
|
}
|
|
112
121
|
|
|
@@ -118,26 +127,27 @@ soundfilereader::soundfilereader( std::string &url ) :
|
|
|
118
127
|
this->cbwavheader.aio_buf = &this->ourwavheader;
|
|
119
128
|
|
|
120
129
|
for( auto i = 0; i < SOUNDFILENUMBUFFERS; i++ ) {
|
|
121
|
-
|
|
122
|
-
this->
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
130
|
+
aiocb &thisblock = this->cbwavblock[ i ];
|
|
131
|
+
soundbuffer &thisbuffer = this->buffer[ i ];
|
|
132
|
+
|
|
133
|
+
thisbuffer.resize( this->samplecount );
|
|
134
|
+
std::fill_n( thisbuffer.begin(), thisbuffer.size(), 0 );
|
|
135
|
+
|
|
136
|
+
thisblock.aio_nbytes = this->bytecount;
|
|
137
|
+
thisblock.aio_fildes = this->file;
|
|
138
|
+
thisblock.aio_offset = sizeof( wavheader ) + ( i * this->bytecount );
|
|
139
|
+
thisblock.aio_buf = thisbuffer.data();
|
|
126
140
|
}
|
|
127
141
|
|
|
128
142
|
/* read */
|
|
129
143
|
if ( aio_read( &this->cbwavheader ) == -1 ) {
|
|
130
144
|
fprintf( stderr, "aio_read read of header failed in soundfile\n" );
|
|
131
145
|
this->badheader = true;
|
|
146
|
+
releasespinlock( this->filelock );
|
|
132
147
|
return;
|
|
133
148
|
}
|
|
134
149
|
|
|
135
|
-
|
|
136
|
-
fprintf( stderr, "aio_read read of block failed in soundfile" );
|
|
137
|
-
this->bodyread = true;
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
150
|
+
releasespinlock( this->filelock );
|
|
141
151
|
return;
|
|
142
152
|
}
|
|
143
153
|
|
|
@@ -156,6 +166,86 @@ soundfilereader::pointer soundfilereader::create( std::string url ) {
|
|
|
156
166
|
return pointer( new soundfilereader( url ) );
|
|
157
167
|
}
|
|
158
168
|
|
|
169
|
+
void soundfilereader::parseheader( void ) {
|
|
170
|
+
if( this->headerread ) return;
|
|
171
|
+
|
|
172
|
+
if( aio_error( &this->cbwavheader ) == EINPROGRESS ) {
|
|
173
|
+
fprintf( stderr, "Read of soundfile wav header has not completed\n" );
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this->headerread = true;
|
|
178
|
+
|
|
179
|
+
if( 'W' != this->ourwavheader.wave_header[ 0 ] ) {
|
|
180
|
+
this->badheader = true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
switch( this->ourwavheader.sample_rate ) {
|
|
184
|
+
case 8000:
|
|
185
|
+
case 16000:
|
|
186
|
+
break;
|
|
187
|
+
default:
|
|
188
|
+
fprintf( stderr, "Bad sample rate in wav\n" );
|
|
189
|
+
this->badheader = true;
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this->ploadtype = L168KPAYLOADTYPE;
|
|
194
|
+
this->bytecount = L16NARROWBANDBYTES;
|
|
195
|
+
this->samplecount = L16PAYLOADSAMPLES;
|
|
196
|
+
switch( this->ourwavheader.audio_format ) {
|
|
197
|
+
case WAVE_FORMAT_PCM: {
|
|
198
|
+
if( 8000 == this->ourwavheader.sample_rate ) {
|
|
199
|
+
this->ploadtype = L168KPAYLOADTYPE;
|
|
200
|
+
this->bytecount = L16NARROWBANDBYTES;
|
|
201
|
+
this->samplecount = L16PAYLOADSAMPLES;
|
|
202
|
+
} else if( 16000 == this->ourwavheader.sample_rate ) {
|
|
203
|
+
this->ploadtype = L1616KPAYLOADTYPE;
|
|
204
|
+
this->bytecount = L16WIDEBANDBYTES;
|
|
205
|
+
this->samplecount = L1616PAYLOADSAMPLES;
|
|
206
|
+
} else {
|
|
207
|
+
this->badheader = true;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
case WAVE_FORMAT_ALAW: {
|
|
213
|
+
this->ploadtype = PCMAPAYLOADTYPE;
|
|
214
|
+
this->bytecount = G711PAYLOADBYTES;
|
|
215
|
+
this->samplecount = G711PAYLOADSAMPLES;
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case WAVE_FORMAT_MULAW: {
|
|
219
|
+
this->ploadtype = PCMUPAYLOADTYPE;
|
|
220
|
+
this->bytecount = G711PAYLOADBYTES;
|
|
221
|
+
this->samplecount = G711PAYLOADSAMPLES;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
case WAVE_FORMAT_POLYCOM_G722: {
|
|
225
|
+
this->ploadtype = G722PAYLOADTYPE;
|
|
226
|
+
this->bytecount = G722PAYLOADBYTES;
|
|
227
|
+
this->samplecount = G722PAYLOADSAMPLES;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case WAVE_FORMAT_GLOBAL_IP_ILBC: {
|
|
231
|
+
this->ploadtype = ILBCPAYLOADTYPE;
|
|
232
|
+
this->bytecount = ILBC20PAYLOADBYTES;
|
|
233
|
+
this->samplecount = ILBC20PAYLOADSAMPLES;
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
default: {
|
|
237
|
+
this->badheader = true;
|
|
238
|
+
fprintf( stderr, "Bad audio format in wav\n" );
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
auto thisindex = this->currentcbindex % SOUNDFILENUMBUFFERS;
|
|
243
|
+
aiocb &thisblock = this->cbwavblock[ thisindex ];
|
|
244
|
+
if ( aio_read( &thisblock ) == -1 ) {
|
|
245
|
+
this->bodyread = true;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
159
249
|
/*
|
|
160
250
|
## read
|
|
161
251
|
Asynchronous read.
|
|
@@ -167,102 +257,43 @@ We only support 1 channel. Anything else we need to look at.
|
|
|
167
257
|
*/
|
|
168
258
|
bool soundfilereader::read( rawsound &out ) {
|
|
169
259
|
|
|
170
|
-
|
|
260
|
+
SpinLockGuard guard( this->filelock );
|
|
261
|
+
|
|
262
|
+
/* check */
|
|
171
263
|
if ( -1 == this->file ) {
|
|
172
264
|
fprintf( stderr, "No file for open wav sound\n" );
|
|
173
265
|
return false;
|
|
174
266
|
}
|
|
175
267
|
|
|
176
|
-
|
|
268
|
+
this->parseheader();
|
|
269
|
+
|
|
270
|
+
if( this->headerread && this->badheader ) {
|
|
177
271
|
fprintf( stderr, "Bad wav file\n" );
|
|
178
|
-
|
|
179
|
-
return true;
|
|
272
|
+
return false;
|
|
180
273
|
}
|
|
181
274
|
|
|
182
275
|
if( !this->headerread ) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
out.zero();
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if( 'W' != this->ourwavheader.wave_header[ 0 ] ) {
|
|
190
|
-
this->badheader = true;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
switch( this->ourwavheader.sample_rate ) {
|
|
194
|
-
case 8000:
|
|
195
|
-
case 16000:
|
|
196
|
-
break;
|
|
197
|
-
default:
|
|
198
|
-
fprintf( stderr, "Bad sample rate in wav\n" );
|
|
199
|
-
this->badheader = true;
|
|
200
|
-
out.zero();
|
|
201
|
-
return true;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
this->ploadtype = L168KPAYLOADTYPE;
|
|
205
|
-
this->blocksize = L16NARROWBANDBYTES;
|
|
206
|
-
switch( this->ourwavheader.audio_format ) {
|
|
207
|
-
case WAVE_FORMAT_PCM: {
|
|
208
|
-
if( 8000 == this->ourwavheader.sample_rate ) {
|
|
209
|
-
this->ploadtype = L168KPAYLOADTYPE;
|
|
210
|
-
this->blocksize = L16NARROWBANDBYTES;
|
|
211
|
-
} else if( 16000 == this->ourwavheader.sample_rate ) {
|
|
212
|
-
this->ploadtype = L1616KPAYLOADTYPE;
|
|
213
|
-
this->blocksize = L16WIDEBANDBYTES;
|
|
214
|
-
} else {
|
|
215
|
-
this->badheader = true;
|
|
216
|
-
out.zero();
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
case WAVE_FORMAT_ALAW: {
|
|
222
|
-
this->ploadtype = PCMAPAYLOADTYPE;
|
|
223
|
-
this->blocksize = G711PAYLOADBYTES;
|
|
224
|
-
break;
|
|
225
|
-
}
|
|
226
|
-
case WAVE_FORMAT_MULAW: {
|
|
227
|
-
this->ploadtype = PCMUPAYLOADTYPE;
|
|
228
|
-
this->blocksize = G711PAYLOADBYTES;
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
case WAVE_FORMAT_POLYCOM_G722: {
|
|
232
|
-
this->ploadtype = G722PAYLOADTYPE;
|
|
233
|
-
this->blocksize = G722PAYLOADBYTES;
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
case WAVE_FORMAT_GLOBAL_IP_ILBC: {
|
|
237
|
-
this->ploadtype = ILBCPAYLOADTYPE;
|
|
238
|
-
this->blocksize = ILBC20PAYLOADBYTES;
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
default: {
|
|
242
|
-
this->badheader = true;
|
|
243
|
-
out.zero();
|
|
244
|
-
fprintf( stderr, "Bad audio format in wav\n" );
|
|
245
|
-
return true;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
this->headerread = true;
|
|
276
|
+
out.zero();
|
|
277
|
+
return true;
|
|
249
278
|
}
|
|
250
279
|
|
|
251
280
|
if( this->initseekmseconds > 0 ) {
|
|
252
|
-
this->
|
|
281
|
+
this->realsetposition( this->initseekmseconds );
|
|
253
282
|
out.zero();
|
|
254
283
|
return true;
|
|
255
284
|
}
|
|
256
285
|
|
|
257
|
-
|
|
258
|
-
|
|
286
|
+
auto thisindex = this->currentcbindex % SOUNDFILENUMBUFFERS;
|
|
287
|
+
aiocb &thisblock = this->cbwavblock[ thisindex ];
|
|
288
|
+
|
|
289
|
+
if( aio_error( &thisblock ) == EINPROGRESS ) {
|
|
259
290
|
/* return some silence */
|
|
260
291
|
out.zero();
|
|
261
292
|
return true;
|
|
262
293
|
}
|
|
263
294
|
|
|
264
295
|
/* success? */
|
|
265
|
-
|
|
296
|
+
auto numbytes = aio_return( &thisblock );
|
|
266
297
|
|
|
267
298
|
if( -1 == numbytes ) {
|
|
268
299
|
fprintf( stderr, "Bad call to aio_return\n" );
|
|
@@ -271,32 +302,39 @@ bool soundfilereader::read( rawsound &out ) {
|
|
|
271
302
|
return true;
|
|
272
303
|
}
|
|
273
304
|
|
|
274
|
-
uint8_t *current = ( uint8_t * )
|
|
275
|
-
out = rawsound( current, this->
|
|
305
|
+
uint8_t *current = ( uint8_t * ) thisblock.aio_buf;
|
|
306
|
+
out = rawsound( current, this->samplecount, this->ploadtype, this->ourwavheader.sample_rate );
|
|
276
307
|
|
|
277
|
-
if( numbytes < this->
|
|
308
|
+
if( static_cast<size_t>( numbytes ) < this->bytecount ) {
|
|
278
309
|
this->bodyread = true;
|
|
279
310
|
/* TODO if we get a partial read we probably should not zero that part */
|
|
280
311
|
out.zero();
|
|
312
|
+
return true;
|
|
281
313
|
}
|
|
282
314
|
|
|
283
315
|
/* Get the next block reading */
|
|
284
|
-
auto lastreadoffset =
|
|
316
|
+
auto lastreadoffset = thisblock.aio_offset;
|
|
285
317
|
this->currentcbindex = ( this->currentcbindex + 1 ) % SOUNDFILENUMBUFFERS;
|
|
286
318
|
|
|
287
|
-
|
|
288
|
-
|
|
319
|
+
aiocb &nextblock = this->cbwavblock[ this->currentcbindex ];
|
|
320
|
+
soundbuffer &thisbuffer = this->buffer[ this->currentcbindex ];
|
|
321
|
+
|
|
322
|
+
/* it shouldn't reallocate - but just in case */
|
|
323
|
+
thisbuffer.resize( this->bytecount );
|
|
324
|
+
nextblock.aio_buf = thisbuffer.data();
|
|
325
|
+
|
|
326
|
+
if( nextblock.aio_offset > this->ourwavheader.chunksize ) {
|
|
327
|
+
nextblock.aio_offset = sizeof( wavheader );
|
|
289
328
|
} else {
|
|
290
|
-
|
|
329
|
+
nextblock.aio_offset = lastreadoffset + this->bytecount;
|
|
291
330
|
}
|
|
292
|
-
|
|
293
|
-
|
|
331
|
+
|
|
332
|
+
nextblock.aio_nbytes = this->bytecount;
|
|
294
333
|
|
|
295
334
|
/* read next block */
|
|
296
|
-
if (
|
|
335
|
+
if ( aio_read( &nextblock ) == -1 ) {
|
|
297
336
|
this->bodyread = true;
|
|
298
|
-
|
|
299
|
-
return true;
|
|
337
|
+
return false;
|
|
300
338
|
}
|
|
301
339
|
|
|
302
340
|
return true;
|
|
@@ -307,63 +345,74 @@ bool soundfilereader::read( rawsound &out ) {
|
|
|
307
345
|
Have we completed reading the file.
|
|
308
346
|
*/
|
|
309
347
|
bool soundfilereader::complete( void ) {
|
|
348
|
+
SpinLockGuard guard( this->filelock );
|
|
349
|
+
|
|
310
350
|
if( this->badheader ) return true;
|
|
311
351
|
if( !this->headerread ) return false;
|
|
312
352
|
if( -1 == this->file ) return true;
|
|
313
353
|
return this->bodyread;
|
|
314
354
|
}
|
|
315
355
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
*/
|
|
321
|
-
void soundfilereader::setposition( long mseconds ) {
|
|
356
|
+
/**
|
|
357
|
+
* Cancel all current block reads recalculate offsets and trigger a new read.
|
|
358
|
+
*/
|
|
359
|
+
void soundfilereader::realsetposition( long mseconds ) {
|
|
322
360
|
|
|
323
|
-
|
|
324
|
-
if ( -1 == this->file ) {
|
|
325
|
-
this->initseekmseconds = mseconds;
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
if( !this->headerread && aio_error( &this->cbwavheader ) == 0 ) {
|
|
330
|
-
this->headerread = true;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if( !this->headerread ) {
|
|
334
|
-
this->initseekmseconds = mseconds;
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
361
|
+
while( AIO_NOTCANCELED == aio_cancel( this->file, NULL ) );
|
|
337
362
|
|
|
338
363
|
this->bodyread = false;
|
|
339
|
-
while( AIO_NOTCANCELED == aio_cancel( this->file, NULL ) );
|
|
340
364
|
|
|
341
|
-
this->currentcbindex =
|
|
365
|
+
this->currentcbindex = 0;
|
|
366
|
+
aiocb &thisblock = this->cbwavblock[ this->currentcbindex ];
|
|
342
367
|
|
|
343
368
|
off_t our_aio_offset = ( this->ourwavheader.bit_depth /*16*/ / 8 ) * ( this->ourwavheader.sample_rate / 1000 ) * mseconds; /* bytes per sample */
|
|
344
|
-
our_aio_offset = ( our_aio_offset / this->
|
|
369
|
+
our_aio_offset = ( our_aio_offset / this->bytecount ) * this->bytecount; /* realign to the nearest block */
|
|
345
370
|
our_aio_offset += sizeof( wavheader );
|
|
346
371
|
|
|
347
372
|
for( auto i = 0; i < SOUNDFILENUMBUFFERS; i++ ) {
|
|
348
|
-
|
|
373
|
+
|
|
374
|
+
aiocb &nextblock = this->cbwavblock[ i ];
|
|
375
|
+
soundbuffer &thisbuffer = this->buffer[ i ];
|
|
376
|
+
|
|
377
|
+
nextblock.aio_offset = our_aio_offset + ( i * this->bytecount );
|
|
378
|
+
thisbuffer.resize( this->bytecount );
|
|
379
|
+
nextblock.aio_buf = thisbuffer.data();
|
|
349
380
|
}
|
|
350
381
|
|
|
351
|
-
|
|
352
|
-
if ( aio_read( &this->cbwavblock[ this->currentcbindex ] ) == -1 ) {
|
|
382
|
+
if ( aio_read( &thisblock ) == -1 ) {
|
|
353
383
|
this->bodyread = true;
|
|
354
384
|
}
|
|
355
385
|
|
|
356
386
|
this->initseekmseconds = 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/*!md
|
|
390
|
+
# setposition and getposition
|
|
391
|
+
Either queue a set positiuon or perform it depending if our header has been read.
|
|
392
|
+
*/
|
|
393
|
+
void soundfilereader::setposition( long mseconds ) {
|
|
357
394
|
|
|
395
|
+
SpinLockGuard guard( this->filelock );
|
|
396
|
+
|
|
397
|
+
/* check */
|
|
398
|
+
if ( -1 == this->file || !this->headerread ) {
|
|
399
|
+
this->initseekmseconds = mseconds;
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
this->realsetposition( mseconds );
|
|
358
404
|
}
|
|
359
405
|
|
|
360
406
|
long soundfilereader::offtomsecs( void ) {
|
|
361
|
-
off_t position = this->cbwavblock[ this->currentcbindex ].aio_offset - sizeof( wavheader );
|
|
407
|
+
off_t position = this->cbwavblock[ this->currentcbindex % SOUNDFILENUMBUFFERS ].aio_offset - sizeof( wavheader );
|
|
362
408
|
return position / ( ( this->ourwavheader.bit_depth / 8 ) * ( this->ourwavheader.sample_rate / 1000 ) );
|
|
363
409
|
}
|
|
364
410
|
|
|
365
411
|
long soundfilereader::getposition( void ) {
|
|
366
|
-
|
|
412
|
+
|
|
413
|
+
SpinLockGuard guard( this->filelock );
|
|
414
|
+
|
|
415
|
+
if( this->cbwavblock[ this->currentcbindex % SOUNDFILENUMBUFFERS ].aio_offset <= ( off_t ) sizeof( wavheader ) ) {
|
|
367
416
|
return 0;
|
|
368
417
|
}
|
|
369
418
|
|
|
@@ -389,6 +438,7 @@ soundfilewriter::soundfilewriter( std::string &url, int16_t numchannels, int32_t
|
|
|
389
438
|
|
|
390
439
|
if ( -1 == this->file ) {
|
|
391
440
|
/* Not much more we can do */
|
|
441
|
+
releasespinlock( this->filelock );
|
|
392
442
|
return;
|
|
393
443
|
}
|
|
394
444
|
|
|
@@ -400,8 +450,12 @@ soundfilewriter::soundfilewriter( std::string &url, int16_t numchannels, int32_t
|
|
|
400
450
|
blocknumbytes = L16WIDEBANDBYTES;
|
|
401
451
|
}
|
|
402
452
|
|
|
453
|
+
if( numchannels != 2 ) {
|
|
454
|
+
numchannels = 1;
|
|
455
|
+
}
|
|
456
|
+
|
|
403
457
|
this->ourwavheader.fmt_chunk_size = 16;
|
|
404
|
-
this->ourwavheader.num_channels = numchannels; /* or 2 */
|
|
458
|
+
this->ourwavheader.num_channels = numchannels; /* 1 or 2 */
|
|
405
459
|
this->ourwavheader.sample_rate = samplerate;
|
|
406
460
|
this->ourwavheader.byte_rate = samplerate * numchannels * this->ourwavheader.bit_depth / 8;
|
|
407
461
|
this->ourwavheader.chunksize = 0;
|
|
@@ -417,9 +471,11 @@ soundfilewriter::soundfilewriter( std::string &url, int16_t numchannels, int32_t
|
|
|
417
471
|
std::cerr << "soundfile unable to write wav header to file " << url << std::endl;
|
|
418
472
|
close( this->file );
|
|
419
473
|
this->file = -1;
|
|
474
|
+
releasespinlock( this->filelock );
|
|
420
475
|
return;
|
|
421
476
|
}
|
|
422
477
|
|
|
478
|
+
releasespinlock( this->filelock );
|
|
423
479
|
return;
|
|
424
480
|
}
|
|
425
481
|
|
|
@@ -448,85 +504,107 @@ This should be called on our tick - 20mS should be ample to complete an async wr
|
|
|
448
504
|
We maintain SOUNDFILENUMBUFFERS to ensure previous writes have an oppertunity to write.
|
|
449
505
|
*/
|
|
450
506
|
bool soundfilewriter::write( codecx &in, codecx &out ) {
|
|
507
|
+
|
|
508
|
+
SpinLockGuard guard( this->filelock );
|
|
509
|
+
|
|
510
|
+
if( -1 == this->file ) {
|
|
511
|
+
fprintf( stderr, "soundfile calling write with no open file!\n" );
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
|
|
451
515
|
int16_t *inbuf = nullptr;
|
|
452
516
|
int16_t *outbuf = nullptr;
|
|
453
|
-
size_t
|
|
454
|
-
|
|
517
|
+
size_t inbufsize = 0; /* count of int16 vals */
|
|
518
|
+
size_t outbufsize = 0;
|
|
519
|
+
int inbytespersample = 1;
|
|
520
|
+
int outbytespersample = 1;
|
|
521
|
+
|
|
522
|
+
auto format = this->getwavformattopt();
|
|
455
523
|
|
|
456
524
|
if( in.hasdata() ) {
|
|
457
|
-
rawsound &inref = in.getref(
|
|
525
|
+
rawsound &inref = in.getref( format );
|
|
458
526
|
if( !inref.isdirty() ) {
|
|
459
527
|
inbuf = ( int16_t * ) inref.c_str();
|
|
460
|
-
|
|
461
|
-
|
|
528
|
+
inbufsize = inref.size();
|
|
529
|
+
inbytespersample = inref.getbytespersample();
|
|
462
530
|
}
|
|
463
531
|
}
|
|
464
532
|
|
|
465
533
|
if( out.hasdata() ) {
|
|
466
|
-
rawsound &outref = out.getref(
|
|
534
|
+
rawsound &outref = out.getref( format );
|
|
467
535
|
if( !outref.isdirty() ) {
|
|
468
536
|
outbuf = ( int16_t * ) outref.c_str();
|
|
469
|
-
|
|
470
|
-
|
|
537
|
+
outbufsize = outref.size();
|
|
538
|
+
outbytespersample = outref.getbytespersample();
|
|
471
539
|
}
|
|
472
540
|
}
|
|
473
541
|
|
|
474
|
-
|
|
542
|
+
/* nothing to do */
|
|
543
|
+
if( nullptr == inbuf && nullptr == outbuf ) {
|
|
475
544
|
return false;
|
|
476
545
|
}
|
|
477
546
|
|
|
478
|
-
|
|
479
|
-
if(
|
|
480
|
-
/* this shouldn't happen */
|
|
481
|
-
fprintf( stderr, "Trying to save larger block than expected - capping\n" );
|
|
482
|
-
bufsize = this->cbwavblock[ this->currentcbindex ].aio_nbytes / bytespersample;
|
|
483
|
-
}
|
|
547
|
+
if( inbytespersample != 2 ) inbytespersample = 1;
|
|
548
|
+
if( outbytespersample != 2 ) outbytespersample = 1;
|
|
484
549
|
|
|
485
|
-
|
|
486
|
-
|
|
550
|
+
auto thisindex = this->currentcbindex % SOUNDFILENUMBUFFERS;
|
|
551
|
+
aiocb &thisblock = this->cbwavblock[ thisindex ];
|
|
552
|
+
soundbuffer &thisbuffer = this->buffer[ thisindex ];
|
|
553
|
+
|
|
554
|
+
inbufsize = std::min( inbufsize, AIOCBSINGLECHANBUFFNUMSAMPLES );
|
|
555
|
+
outbufsize = std::min( outbufsize, AIOCBSINGLECHANBUFFNUMSAMPLES );
|
|
556
|
+
|
|
557
|
+
auto largestbufsize = std::max( inbufsize, outbufsize );
|
|
558
|
+
if( 0 == largestbufsize) {
|
|
487
559
|
return false;
|
|
488
560
|
}
|
|
489
561
|
|
|
490
|
-
if(
|
|
491
|
-
fprintf( stderr, "soundfile
|
|
562
|
+
if( aio_error( &thisblock ) == EINPROGRESS ) {
|
|
563
|
+
fprintf( stderr, "soundfile trying to write a packet whilst last is still in progress\n" );
|
|
492
564
|
return false;
|
|
493
565
|
}
|
|
494
566
|
|
|
495
|
-
|
|
496
|
-
|
|
567
|
+
auto numchannels = this->ourwavheader.num_channels;
|
|
568
|
+
if( numchannels != 2 ) numchannels = 1;
|
|
569
|
+
|
|
570
|
+
thisbuffer.resize( largestbufsize * numchannels );
|
|
571
|
+
std::fill_n( thisbuffer.begin(), thisbuffer.size(), 0 );
|
|
572
|
+
auto buf = thisbuffer.data();
|
|
573
|
+
thisblock.aio_buf = buf;
|
|
497
574
|
|
|
498
|
-
|
|
499
|
-
|
|
575
|
+
thisblock.aio_nbytes = largestbufsize * inbytespersample * numchannels;
|
|
576
|
+
thisblock.aio_offset = sizeof( wavheader ) +
|
|
577
|
+
( this->tickcount * thisblock.aio_nbytes );
|
|
500
578
|
|
|
501
|
-
if( nullptr != inbuf ) {
|
|
502
|
-
for(
|
|
579
|
+
if( inbufsize > 0 && nullptr != inbuf ) {
|
|
580
|
+
for (size_t i = 0; i < inbufsize; i++) {
|
|
503
581
|
*buf = *inbuf;
|
|
504
582
|
inbuf++;
|
|
505
|
-
buf +=
|
|
583
|
+
buf += numchannels;
|
|
506
584
|
}
|
|
507
585
|
}
|
|
508
586
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
587
|
+
if( outbufsize > 0 && nullptr != outbuf ) {
|
|
588
|
+
/* start at the beggining again */
|
|
589
|
+
buf = thisbuffer.data();
|
|
590
|
+
/* only works up to 2 channels - which is all we support */
|
|
591
|
+
if( numchannels == 2 ) buf++;
|
|
514
592
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
*buf +=
|
|
518
|
-
outbuf
|
|
519
|
-
buf +=
|
|
593
|
+
for (size_t i = 0; i < outbufsize; i++) {
|
|
594
|
+
auto outval = *outbuf; /////////////////// this is our crash
|
|
595
|
+
*buf += outval;
|
|
596
|
+
outbuf++;
|
|
597
|
+
buf += numchannels;
|
|
520
598
|
}
|
|
521
599
|
}
|
|
522
600
|
|
|
523
|
-
if ( aio_write( &
|
|
601
|
+
if ( aio_write( &thisblock ) == -1 ) {
|
|
524
602
|
fprintf( stderr, "soundfile unable to write wav block to file %s\n", this->url.c_str() );
|
|
525
603
|
return false;
|
|
526
604
|
}
|
|
527
605
|
|
|
528
606
|
uint32_t maxbasedonthischunk = 0;
|
|
529
|
-
maxbasedonthischunk =
|
|
607
|
+
maxbasedonthischunk = thisblock.aio_offset + thisblock.aio_nbytes;
|
|
530
608
|
if( maxbasedonthischunk > this->ourwavheader.subchunksize ) {
|
|
531
609
|
this->ourwavheader.subchunksize = maxbasedonthischunk;
|
|
532
610
|
this->ourwavheader.chunksize = maxbasedonthischunk + 36;
|