@babblevoice/projectrtp 2.5.22 → 2.5.26

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/README.md CHANGED
@@ -60,7 +60,7 @@ If you wish to build outsode of a Docker image, there are npm target scripts for
60
60
 
61
61
  ```bash
62
62
  docker buildx prune
63
- docker buildx build --platform linux/amd64,linux/arm64 -t tinpotnick/projectrtp:2.4.20_beta1 . --push
63
+ docker buildx build --platform linux/amd64,linux/arm64 -t tinpotnick/projectrtp:2.5.23_beta4 . --push
64
64
  ```
65
65
 
66
66
  ## Example scripts
package/lib/server.js CHANGED
@@ -277,7 +277,15 @@ class channel {
277
277
 
278
278
  options.channel = "open"
279
279
 
280
- const resolvepromise = new Promise( ( resolve ) => {
280
+ const resolvepromise = new Promise( ( resolve, reject ) => {
281
+
282
+ if ( 0 < nodes.size ) {
283
+ const node = randomenode( options.nodeinstance )
284
+ const request = JSON.parse( JSON.stringify( options ) )
285
+ request.channel = "open"
286
+ channel._createforlisten( request, node, cb, resolve, reject )
287
+ return
288
+ }
281
289
 
282
290
  const newchannel = new channel()
283
291
  if( cb ) newchannel.em.on( "all", cb )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/projectrtp",
3
- "version": "2.5.22",
3
+ "version": "2.5.26",
4
4
  "description": "A scalable Node addon RTP server",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -655,7 +655,7 @@ bool projectrtpchannel::checkfordtmf( rtppacket *src ) {
655
655
  uint16_t sn = src->getsequencenumber();
656
656
 
657
657
  if( 0 != this->rfc2833pt &&
658
- src->getpayloadtype() == this->rfc2833pt ) {
658
+ src->getpayloadtype() == RFC2833PAYLOADTYPE ) {
659
659
 
660
660
  if( src->getpayloadlength() >= 4 ) {
661
661
  /* We have to look for DTMF events handling issues like missing events - such as the marker or end bit */
@@ -811,7 +811,8 @@ void projectrtpchannel::correctaddress( void ) {
811
811
  */
812
812
  void projectrtpchannel::correctssrc( uint32_t ssrc ) {
813
813
  if( !this->receivedrtp ) {
814
- this->receivedrtp = true;
814
+ /* allow settling */
815
+ if( this->inbuff->getpushed() > 5 )this->receivedrtp = true;
815
816
  this->ssrcin = ssrc;
816
817
  }
817
818
  }
@@ -871,15 +872,24 @@ void projectrtpchannel::readsomertp( void ) {
871
872
  goto readsomemore;
872
873
  }
873
874
 
874
- if( !this->recv ) {
875
+ /* TODO ZRTP? */
876
+ if( buf->getpacketextension() ) {
875
877
  this->receivedpkskip++;
876
878
  goto readsomemore;
877
879
  }
878
880
 
879
- this->tickswithnortpcount = 0;
880
-
881
881
  this->correctaddress();
882
+
883
+ if( !this->recv ) {
884
+ this->receivedpkskip++;
885
+ goto readsomemore;
886
+ }
887
+
888
+ /*
889
+ after speaking with Magrathea - more streams are changing ssrc without notice mid-stream
890
+ for now do not check.
882
891
  this->correctssrc( buf->getssrc() );
892
+ */
883
893
 
884
894
  if( this->confirmedrtpsenderendpoint != this->rtpsenderendpoint ) {
885
895
  /* After the first packet - we only accept data from the verified source */
@@ -887,10 +897,14 @@ void projectrtpchannel::readsomertp( void ) {
887
897
  goto readsomemore;
888
898
  }
889
899
 
900
+ /*
890
901
  if( buf->getssrc() != this->ssrcin ) {
891
902
  this->receivedpkskip++;
892
903
  goto readsomemore;
893
904
  }
905
+ */
906
+
907
+ this->tickswithnortpcount = 0;
894
908
 
895
909
  /* dynamic payload types */
896
910
  auto pt = buf->getpayloadtype();
@@ -325,8 +325,8 @@ void projectchannelmux::postrtpdata( projectrtpchannelptr srcchan, projectrtpcha
325
325
  return;
326
326
  }
327
327
 
328
- if( src->getpayloadtype() == srcchan->rfc2833pt ) {
329
- dst->setpayloadtype( srcchan->rfc2833pt );
328
+ if( src->getpayloadtype() == RFC2833PAYLOADTYPE ) {
329
+ dst->setpayloadtype( dstchan->rfc2833pt );
330
330
  dst->copy( src );
331
331
  } else {
332
332
  srcchan->incodec << codecx::next;
@@ -438,10 +438,16 @@ Search for the relevent data and convert as necessary.
438
438
  */
439
439
  bool codecx::requirewideband( void ) {
440
440
  if( 0 != this->l1616kref.size() && !this->l1616kref.isdirty() ) return true;
441
+
442
+ if( 0 != this->l168kref.size() && !this->l168kref.isdirty() ) {
443
+ return this->l16lowtowideband();
444
+ }
445
+
441
446
  if( this->g722tol16() ) return true;
442
- if( !this->g711tol16() )
443
- {
444
- if( !this->ilbctol16() ) return false;
447
+ if( !this->g711tol16() ) {
448
+ if( !this->ilbctol16() ) {
449
+ return false;
450
+ }
445
451
  }
446
452
 
447
453
  return this->l16lowtowideband();
@@ -477,9 +483,13 @@ bool codecx::l16widetonarrowband( void ) {
477
483
  ## requirenarrowband
478
484
  Search for the relevent data and convert as necessary.
479
485
  */
480
- bool codecx::requirenarrowband( void )
481
- {
486
+ bool codecx::requirenarrowband( void ) {
482
487
  if( 0 != this->l168kref.size() && !this->l168kref.isdirty() ) return true;
488
+
489
+ if( 0 != this->l1616kref.size() && !this->l1616kref.isdirty() ) {
490
+ return this->l16widetonarrowband();
491
+ }
492
+
483
493
  if( this->g711tol16() ) return true;
484
494
  if( this->ilbctol16() ) return true;
485
495
  this->g722tol16();
@@ -101,6 +101,7 @@ soundfilereader::soundfilereader( std::string &url ) :
101
101
  blocksize( L16NARROWBANDBYTES ),
102
102
  badheader( false ),
103
103
  headerread( false ),
104
+ bodyread( false ),
104
105
  initseekmseconds( 0 ),
105
106
  ploadtype( L168KPAYLOADTYPE ) {
106
107
 
@@ -127,15 +128,13 @@ soundfilereader::soundfilereader( std::string &url ) :
127
128
  /* read */
128
129
  if ( aio_read( &this->cbwavheader ) == -1 ) {
129
130
  fprintf( stderr, "aio_read read of header failed in soundfile\n" );
130
- close( this->file );
131
- this->file = -1;
131
+ this->badheader = true;
132
132
  return;
133
133
  }
134
134
 
135
135
  if ( aio_read( &this->cbwavblock[ this->currentcbindex ] ) == -1 ) {
136
136
  fprintf( stderr, "aio_read read of block failed in soundfile" );
137
- close( this->file );
138
- this->file = -1;
137
+ this->bodyread = true;
139
138
  return;
140
139
  }
141
140
 
@@ -153,8 +152,7 @@ soundfilereader::~soundfilereader() {
153
152
  # create
154
153
  Shared pointer version of us.
155
154
  */
156
- soundfilereader::pointer soundfilereader::create( std::string url )
157
- {
155
+ soundfilereader::pointer soundfilereader::create( std::string url ) {
158
156
  return pointer( new soundfilereader( url ) );
159
157
  }
160
158
 
@@ -177,16 +175,17 @@ bool soundfilereader::read( rawsound &out ) {
177
175
 
178
176
  if( this->badheader ) {
179
177
  fprintf( stderr, "Bad wav file\n" );
180
- return false;
181
- }
182
-
183
- if( !this->headerread &&
184
- aio_error( &this->cbwavheader ) == EINPROGRESS ) {
185
- fprintf( stderr, "Read of soundfile wav header has not completed\n" );
186
- return false;
178
+ out.zero();
179
+ return true;
187
180
  }
188
181
 
189
182
  if( !this->headerread ) {
183
+ if( aio_error( &this->cbwavheader ) == EINPROGRESS ) {
184
+ fprintf( stderr, "Read of soundfile wav header has not completed\n" );
185
+ out.zero();
186
+ return true;
187
+ }
188
+
190
189
  if( 'W' != this->ourwavheader.wave_header[ 0 ] ) {
191
190
  this->badheader = true;
192
191
  }
@@ -197,7 +196,9 @@ bool soundfilereader::read( rawsound &out ) {
197
196
  break;
198
197
  default:
199
198
  fprintf( stderr, "Bad sample rate in wav\n" );
200
- return false;
199
+ this->badheader = true;
200
+ out.zero();
201
+ return true;
201
202
  }
202
203
 
203
204
  this->ploadtype = L168KPAYLOADTYPE;
@@ -211,7 +212,9 @@ bool soundfilereader::read( rawsound &out ) {
211
212
  this->ploadtype = L1616KPAYLOADTYPE;
212
213
  this->blocksize = L16WIDEBANDBYTES;
213
214
  } else {
214
- return false;
215
+ this->badheader = true;
216
+ out.zero();
217
+ return true;
215
218
  }
216
219
  break;
217
220
  }
@@ -236,8 +239,10 @@ bool soundfilereader::read( rawsound &out ) {
236
239
  break;
237
240
  }
238
241
  default: {
242
+ this->badheader = true;
243
+ out.zero();
239
244
  fprintf( stderr, "Bad audio format in wav\n" );
240
- return false;
245
+ return true;
241
246
  }
242
247
  }
243
248
  this->headerread = true;
@@ -245,12 +250,15 @@ bool soundfilereader::read( rawsound &out ) {
245
250
 
246
251
  if( this->initseekmseconds > 0 ) {
247
252
  this->setposition( this->initseekmseconds );
253
+ out.zero();
248
254
  return true;
249
255
  }
250
256
 
251
257
  if( aio_error( &this->cbwavblock[ this->currentcbindex ] ) == EINPROGRESS ) {
252
258
  fprintf( stderr, "Read of soundfile wav block has not completed\n" );
253
- return false;
259
+ /* return some silence */
260
+ out.zero();
261
+ return true;
254
262
  }
255
263
 
256
264
  /* success? */
@@ -258,12 +266,20 @@ bool soundfilereader::read( rawsound &out ) {
258
266
 
259
267
  if( -1 == numbytes ) {
260
268
  fprintf( stderr, "Bad call to aio_return\n" );
261
- return false;
269
+ this->bodyread = true;
270
+ out.zero();
271
+ return true;
262
272
  }
263
273
 
264
274
  uint8_t *current = ( uint8_t * ) this->cbwavblock[ this->currentcbindex ].aio_buf;
265
275
  out = rawsound( current, this->blocksize, this->ploadtype, this->ourwavheader.sample_rate );
266
276
 
277
+ if( numbytes < this->blocksize ) {
278
+ this->bodyread = true;
279
+ /* TODO if we get a partial read we probably should not zero that part */
280
+ out.zero();
281
+ }
282
+
267
283
  /* Get the next block reading */
268
284
  auto lastreadoffset = this->cbwavblock[ this->currentcbindex ].aio_offset;
269
285
  this->currentcbindex = ( this->currentcbindex + 1 ) % SOUNDFILENUMBUFFERS;
@@ -277,10 +293,10 @@ bool soundfilereader::read( rawsound &out ) {
277
293
  this->cbwavblock[ this->currentcbindex ].aio_nbytes = this->blocksize;
278
294
 
279
295
  /* read next block */
280
- if ( aio_read( &this->cbwavblock[ this->currentcbindex ] ) == -1 ) {
281
- close( this->file );
282
- this->file = -1;
283
- return false;
296
+ if ( !this->bodyread && aio_read( &this->cbwavblock[ this->currentcbindex ] ) == -1 ) {
297
+ this->bodyread = true;
298
+ out.zero();
299
+ return true;
284
300
  }
285
301
 
286
302
  return true;
@@ -291,11 +307,10 @@ bool soundfilereader::read( rawsound &out ) {
291
307
  Have we completed reading the file.
292
308
  */
293
309
  bool soundfilereader::complete( void ) {
294
- if( this->badheader || !this->headerread ) {
295
- return false;
296
- }
297
-
298
- return ( this->cbwavblock[ this->currentcbindex ].aio_offset + this->blocksize ) > this->ourwavheader.chunksize;
310
+ if( this->badheader ) return true;
311
+ if( !this->headerread ) return false;
312
+ if( -1 == this->file ) return true;
313
+ return this->bodyread;
299
314
  }
300
315
 
301
316
  /*!md
@@ -315,35 +330,31 @@ void soundfilereader::setposition( long mseconds ) {
315
330
  this->headerread = true;
316
331
  }
317
332
 
333
+ if( !this->headerread ) {
334
+ this->initseekmseconds = mseconds;
335
+ return;
336
+ }
318
337
 
319
- if( this->headerread ) {
320
- while( AIO_NOTCANCELED == aio_cancel( this->file, NULL ) );
338
+ this->bodyread = false;
339
+ while( AIO_NOTCANCELED == aio_cancel( this->file, NULL ) );
321
340
 
322
- this->currentcbindex = ( this->currentcbindex + 1 ) % SOUNDFILENUMBUFFERS;
341
+ this->currentcbindex = ( this->currentcbindex + 1 ) % SOUNDFILENUMBUFFERS;
323
342
 
324
- off_t our_aio_offset = ( this->ourwavheader.bit_depth /*16*/ / 8 ) * ( this->ourwavheader.sample_rate / 1000 ) * mseconds; /* bytes per sample */
325
- our_aio_offset = ( our_aio_offset / this->blocksize ) * this->blocksize; /* realign to the nearest block */
326
- our_aio_offset += sizeof( wavheader );
343
+ 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->blocksize ) * this->blocksize; /* realign to the nearest block */
345
+ our_aio_offset += sizeof( wavheader );
327
346
 
328
- for( auto i = 0; i < SOUNDFILENUMBUFFERS; i++ ) {
329
- memset( &this->cbwavblock[ i ], 0, sizeof( aiocb ) );
330
- this->cbwavblock[ i ].aio_nbytes = L16WIDEBANDBYTES;
331
- this->cbwavblock[ i ].aio_fildes = this->file;
332
- this->cbwavblock[ i ].aio_offset = sizeof( wavheader ) + our_aio_offset + ( i * L16WIDEBANDBYTES );
333
- this->cbwavblock[ i ].aio_buf = this->buffer + ( i * L16WIDEBANDBYTES );
334
- }
335
-
336
- /* read ahead */
337
- this->currentcbindex = 0;
338
- if ( aio_read( &this->cbwavblock[ this->currentcbindex ] ) == -1 ) {
339
- close( this->file );
340
- this->file = -1;
341
- }
347
+ for( auto i = 0; i < SOUNDFILENUMBUFFERS; i++ ) {
348
+ this->cbwavblock[ ( this->currentcbindex + i ) % SOUNDFILENUMBUFFERS ].aio_offset = our_aio_offset + ( i * L16WIDEBANDBYTES );
349
+ }
342
350
 
343
- this->initseekmseconds = 0;
344
- } else {
345
- this->initseekmseconds = mseconds;
351
+ /* read ahead */
352
+ if ( aio_read( &this->cbwavblock[ this->currentcbindex ] ) == -1 ) {
353
+ this->bodyread = true;
346
354
  }
355
+
356
+ this->initseekmseconds = 0;
357
+
347
358
  }
348
359
 
349
360
  long soundfilereader::offtomsecs( void ) {
@@ -423,8 +434,7 @@ soundfilewriter::~soundfilewriter() {
423
434
  # create
424
435
  Shared pointer for writing.
425
436
  */
426
- soundfilewriter::pointer soundfilewriter::create( std::string &url, int16_t numchannels, int32_t samplerate )
427
- {
437
+ soundfilewriter::pointer soundfilewriter::create( std::string &url, int16_t numchannels, int32_t samplerate ) {
428
438
  return pointer( new soundfilewriter( url, numchannels, samplerate ) );
429
439
  }
430
440
 
@@ -119,6 +119,7 @@ private:
119
119
  int blocksize;
120
120
  bool badheader;
121
121
  bool headerread;
122
+ bool bodyread;
122
123
  long initseekmseconds;
123
124
  int ploadtype;
124
125
  };
@@ -5,6 +5,7 @@ const dgram = require( "dgram" )
5
5
  const projectrtp = require( "../../index" ).projectrtp
6
6
  const node = require( "../../lib/node" ).interface
7
7
  const server = require( "../../lib/server" ).interface
8
+ const rtputil = require( "../util/rtp" )
8
9
 
9
10
  /**
10
11
  * Common channel tester
@@ -360,15 +361,9 @@ describe( "rtpchannel", function() {
360
361
  let totalsndiff = 0
361
362
  let totaltsdiff = 0
362
363
  server.on( "message", function( msg ) {
363
- let sn = 0
364
- sn = msg[ 2 ] << 8
365
- sn = sn | msg[ 3 ]
366
-
367
- let ts = 0
368
- ts = msg[ 4 ] << 24
369
- ts = ts | ( msg[ 5 ] << 16 )
370
- ts = ts | ( msg[ 6 ] << 8 )
371
- ts = ts | msg[ 7 ]
364
+ const pk = rtputil.parsepk( msg )
365
+ const sn = pk.sn
366
+ const ts = pk.ts
372
367
 
373
368
  if( -1 !== lastsn ) {
374
369
  totalsndiff += sn - lastsn - 1
@@ -66,16 +66,14 @@ function sendpk( sn, ts, sendtime, dstport, server, pt = 0, ssrc ) {
66
66
  /*
67
67
 
68
68
  */
69
- function senddtmf( sn, ts, sendtime, dstport, server, endofevent, ev ) {
69
+ function senddtmf( sn, ts, sendtime, dstport, server, endofevent, ev, pt = 101 ) {
70
70
 
71
71
  return setTimeout( () => {
72
72
  const ssrc = 25
73
- const pklength = 58
73
+ const pklength = 16
74
74
 
75
75
  const header = Buffer.alloc( pklength )
76
76
 
77
- const pt = 101
78
-
79
77
  header.writeUInt8( 0x80 )
80
78
  header.writeUInt8( pt, 1 ) // payload type
81
79
  header.writeUInt16BE( ( sn ) % ( 2**16 ), 2 )
@@ -715,6 +713,149 @@ describe( "dtmf", function() {
715
713
  } )
716
714
 
717
715
 
716
+ it( "mix 2 channels - pcmu <-> pcma and send DTMF different 2833 pt", async function() {
717
+
718
+ /*
719
+ When mixing 2 channels, we expect the second leg to receive the 2833 packets
720
+ and our server to emit events indicating the DTMF on the first channel.
721
+ */
722
+ this.timeout( 3000 )
723
+ this.slow( 2000 )
724
+
725
+ const endpointa = dgram.createSocket( "udp4" )
726
+ const endpointb = dgram.createSocket( "udp4" )
727
+
728
+ const receivedmessages = []
729
+
730
+ let endpointapkcount = 0
731
+ let endpointbpkcount = 0
732
+ let dtmfpkcount = 0
733
+
734
+ endpointa.on( "message", function( msg ) {
735
+ endpointapkcount++
736
+ expect( msg.length ).to.equal( 172 )
737
+ expect( 0x7f & msg [ 1 ] ).to.equal( 0 )
738
+ } )
739
+
740
+ endpointb.on( "message", function( msg ) {
741
+ endpointbpkcount++
742
+ if( 101 == ( 0x7f & msg [ 1 ] ) ) {
743
+ dtmfpkcount++
744
+ } else {
745
+ expect( msg.length ).to.equal( 172 )
746
+ expect( 0x7f & msg [ 1 ] ).to.equal( 8 )
747
+ endpointb.send( msg, channelb.local.port, "localhost" )
748
+ }
749
+ } )
750
+
751
+ endpointa.bind()
752
+ await new Promise( ( resolve ) => { endpointa.on( "listening", function() { resolve() } ) } )
753
+
754
+ endpointb.bind()
755
+ await new Promise( ( resolve ) => { endpointb.on( "listening", function() { resolve() } ) } )
756
+
757
+ let done
758
+ const finished = new Promise( ( r ) => { done = r } )
759
+
760
+ const channela = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointa.address().port, "codec": 0, "rfc2833pt": 127 } }, function( d ) {
761
+ receivedmessages.push( d )
762
+
763
+ if( "close" === d.action ) channelb.close()
764
+ } )
765
+
766
+ const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 8, "rfc2833pt": 101 } }, function( d ) {
767
+ if( "close" === d.action ) done()
768
+ } )
769
+
770
+ /* mix */
771
+ expect( channela.mix( channelb ) ).to.be.true
772
+
773
+ /* send a packet every 20mS x 50 */
774
+ sendpk( 0, 0, 0, channela.local.port, endpointa )
775
+ sendpk( 1, 1*160, 1*20, channela.local.port, endpointa )
776
+ sendpk( 2, 2*160, 2*20, channela.local.port, endpointa )
777
+ sendpk( 3, 3*160, 3*20, channela.local.port, endpointa )
778
+ sendpk( 4, 4*160, 4*20, channela.local.port, endpointa )
779
+ sendpk( 5, 5*160, 5*20, channela.local.port, endpointa )
780
+ sendpk( 6, 6*160, 6*20, channela.local.port, endpointa )
781
+ sendpk( 7, 7*160, 7*20, channela.local.port, endpointa )
782
+ sendpk( 8, 8*160, 8*20, channela.local.port, endpointa )
783
+ sendpk( 9, 9*160, 9*20, channela.local.port, endpointa )
784
+ sendpk( 10, 10*160, 10*20, channela.local.port, endpointa )
785
+ sendpk( 11, 11*160, 11*20, channela.local.port, endpointa )
786
+ sendpk( 12, 12*160, 12*20, channela.local.port, endpointa )
787
+
788
+ senddtmf( 13, 13*160, 13*20, channela.local.port, endpointa, false, "4", 127 )
789
+ sendpk( 14, 13*160, 13*20, channela.local.port, endpointa, 0 )
790
+ sendpk( 15, 14*160, 14*20, channela.local.port, endpointa, 0 )
791
+ senddtmf( 16, (15*160)+10, (15*20)+10, channela.local.port, endpointa, false, "4", 127 )
792
+ sendpk( 17, 15*160, 15*20, channela.local.port, endpointa, 0 )
793
+ sendpk( 18, 16*160, 16*20, channela.local.port, endpointa, 0 )
794
+ senddtmf( 19, (17*160)+20, (17*20)+20, channela.local.port, endpointa, true, "4", 127 )
795
+ sendpk( 20, 17*160, 17*20, channela.local.port, endpointa, 0 )
796
+ sendpk( 21, 18*160, 18*20, channela.local.port, endpointa, 0 )
797
+ senddtmf( 22, (18*160)+30, (18*20)+30, channela.local.port, endpointa, true, "4", 127 )
798
+ sendpk( 23, 19*160, 19*20, channela.local.port, endpointa, 0 )
799
+ sendpk( 24, 20*160, 20*20, channela.local.port, endpointa, 0 )
800
+ sendpk( 25, 21*160, 21*20, channela.local.port, endpointa, 0 )
801
+ sendpk( 26, 22*160, 22*20, channela.local.port, endpointa, 0 )
802
+ sendpk( 27, 23*160, 23*20, channela.local.port, endpointa, 0 )
803
+ sendpk( 28, 24*160, 24*20, channela.local.port, endpointa, 0 )
804
+ sendpk( 29, 25*160, 25*20, channela.local.port, endpointa, 0 )
805
+
806
+ senddtmf( 30, 26*160, 26*20, channela.local.port, endpointa, false, "5", 127 )
807
+ sendpk( 31, 26*160, 26*20, channela.local.port, endpointa, 0 )
808
+ sendpk( 32, 27*160, 27*20, channela.local.port, endpointa, 0 )
809
+ senddtmf( 33, (27*160)+10, (27*20)+10, channela.local.port, endpointa, false, "5", 127 )
810
+ sendpk( 34, 28*160, 28*20, channela.local.port, endpointa, 0 )
811
+ sendpk( 35, 29*160, 28*20, channela.local.port, endpointa, 0 )
812
+ senddtmf( 36, (28*160)+20, (28*20)+20, channela.local.port, endpointa, true, "5", 127 )
813
+ sendpk( 37, 30*160, 29*20, channela.local.port, endpointa, 0 )
814
+ sendpk( 38, 31*160, 30*20, channela.local.port, endpointa, 0 )
815
+
816
+ senddtmf( 39, (38*160)+30, (30*20)+30, channela.local.port, endpointa, true, "5", 127 )
817
+ sendpk( 40, 32*160, 31*20, channela.local.port, endpointa, 0 )
818
+ sendpk( 41, 33*160, 32*20, channela.local.port, endpointa, 0 )
819
+
820
+ sendpk( 42, 34*160, 33*20, channela.local.port, endpointa, 0 )
821
+ sendpk( 43, 35*160, 34*20, channela.local.port, endpointa, 0 )
822
+ sendpk( 44, 36*160, 35*20, channela.local.port, endpointa, 0 )
823
+ sendpk( 45, 37*160, 36*20, channela.local.port, endpointa, 0 )
824
+ sendpk( 46, 38*160, 37*20, channela.local.port, endpointa, 0 )
825
+ sendpk( 47, 39*160, 38*20, channela.local.port, endpointa, 0 )
826
+ sendpk( 48, 40*160, 39*20, channela.local.port, endpointa, 0 )
827
+ sendpk( 49, 51*160, 40*20, channela.local.port, endpointa, 0 )
828
+
829
+ await new Promise( ( r ) => { setTimeout( () => r(), 1400 ) } )
830
+
831
+ channela.close()
832
+ endpointa.close()
833
+ endpointb.close()
834
+
835
+ await finished
836
+
837
+ expect( endpointapkcount ).to.be.within( 30, 51 )
838
+ expect( endpointbpkcount ).to.be.within( 30, 51 )
839
+
840
+ expect( receivedmessages.length ).to.equal( 5 )
841
+
842
+ expect( dtmfpkcount ).to.be.within( 4, 8 )
843
+
844
+ expect( receivedmessages[ 0 ].action ).to.equal( "mix" )
845
+ expect( receivedmessages[ 1 ].action ).to.equal( "telephone-event" )
846
+ expect( receivedmessages[ 2 ].action ).to.equal( "telephone-event" )
847
+ expect( receivedmessages[ 3 ].action ).to.equal( "mix" )
848
+ expect( receivedmessages[ 3 ].event ).to.equal( "finished" )
849
+ expect( receivedmessages[ 4 ].action ).to.equal( "close" )
850
+
851
+ expect( receivedmessages[ 0 ].event ).to.equal( "start" )
852
+ expect( receivedmessages[ 1 ].event ).to.equal( "4" )
853
+ expect( receivedmessages[ 2 ].event ).to.equal( "5" )
854
+ expect( receivedmessages[ 3 ].event ).to.equal( "finished" )
855
+
856
+ } )
857
+
858
+
718
859
  it( "mix 3 channels - pcmu <-> pcma and ilbc and send DTMF", async function() {
719
860
 
720
861
  this.timeout( 3000 )
@@ -5,9 +5,8 @@ const expect = require( "chai" ).expect
5
5
  const dgram = require( "dgram" )
6
6
  const projectrtp = require( "../../index.js" ).projectrtp
7
7
 
8
- function sendpk( sn, sendtime, dstport, server, data = undefined ) {
8
+ function sendpk( sn, sendtime, dstport, server, data = undefined, pt = 0, ssrc = 25 ) {
9
9
 
10
- const ssrc = 25
11
10
  const pklength = 172
12
11
 
13
12
  return setTimeout( () => {
@@ -27,8 +26,11 @@ function sendpk( sn, sendtime, dstport, server, data = undefined ) {
27
26
  subheader.writeUInt32BE( ts, 2 )
28
27
  subheader.writeUInt32BE( ssrc, 6 )
29
28
 
29
+ const header = Buffer.from( [ 0x80, 0x00 ] )
30
+ header.writeUInt8( pt, 1 ) // payload type
31
+
30
32
  const rtppacket = Buffer.concat( [
31
- Buffer.from( [ 0x80, 0x00 ] ),
33
+ header,
32
34
  subheader,
33
35
  payload ] )
34
36
 
@@ -101,6 +103,75 @@ describe( "channel mix", function() {
101
103
 
102
104
  } )
103
105
 
106
+ it( "basic mix 2 channels with start 2 packets wrong payload type", async function() {
107
+
108
+ this.timeout( 3000 )
109
+ this.slow( 2000 )
110
+
111
+ const endpointa = dgram.createSocket( "udp4" )
112
+ const endpointb = dgram.createSocket( "udp4" )
113
+
114
+ let endpointapkcount = 0
115
+ let endpointbpkcount = 0
116
+
117
+ endpointa.on( "message", function( msg ) {
118
+ endpointapkcount++
119
+ expect( msg.length ).to.equal( 172 )
120
+ expect( 0x7f & msg [ 1 ] ).to.equal( 8 )
121
+ } )
122
+
123
+ endpointb.on( "message", function( msg ) {
124
+ endpointbpkcount++
125
+
126
+ expect( msg.length ).to.equal( 172 )
127
+ expect( 0x7f & msg [ 1 ] ).to.equal( 0 )
128
+ endpointb.send( msg, channelb.local.port, "localhost" )
129
+ } )
130
+
131
+ endpointa.bind()
132
+ await new Promise( ( r ) => { endpointa.on( "listening", function() { r() } ) } )
133
+
134
+ endpointb.bind()
135
+ await new Promise( ( r ) => { endpointb.on( "listening", function() { r() } ) } )
136
+
137
+ let done
138
+ const finished = new Promise( ( r ) => { done = r } )
139
+
140
+ const channela = await projectrtp.openchannel( {}, function( d ) {
141
+ if( "close" === d.action ) channelb.close()
142
+ } )
143
+
144
+ const channelb = await projectrtp.openchannel( { "remote": { "address": "localhost", "port": endpointb.address().port, "codec": 0 } }, function( d ) {
145
+ if( "close" === d.action ) done()
146
+ } )
147
+
148
+ /* mix */
149
+ expect( channela.mix( channelb ) ).to.be.true
150
+
151
+ /* a problem was highlighted that would remote would be called before rtp stream was updated */
152
+ channela.remote( { "address": "localhost", "port": endpointa.address().port, "codec": 8 } )
153
+
154
+ sendpk( 0, 0, channela.local.port, endpointa )
155
+ sendpk( 1, 1, channela.local.port, endpointa )
156
+
157
+ /* Now, when we send UDP on endpointb it passes through our mix then arrives at endpointa */
158
+ for( let i = 2; 50 > i; i ++ ) {
159
+ sendpk( i, i, channela.local.port, endpointa, undefined, 8, 27 )
160
+ }
161
+
162
+ await new Promise( ( resolve ) => { setTimeout( () => resolve(), 1300 ) } )
163
+
164
+ channela.close()
165
+ endpointa.close()
166
+ endpointb.close()
167
+
168
+ expect( endpointapkcount ).to.be.within( 30, 51 )
169
+ expect( endpointbpkcount ).to.be.within( 30, 51 )
170
+
171
+ await finished
172
+
173
+ } )
174
+
104
175
 
105
176
  it( "mix 2 channels then unmix", async function() {
106
177
 
@@ -900,9 +971,9 @@ describe( "channel mix", function() {
900
971
  endpointb.close()
901
972
  endpointc.close()
902
973
 
903
- expect( endpointapkcountzero ).to.be.within( 65, 75 )
904
- expect( endpointbpkcountzero ).to.be.within( 65, 75 )
905
- expect( endpointcpkcountzero ).to.be.within( 65, 75 )
974
+ expect( endpointapkcountzero ).to.be.within( 60, 75 )
975
+ expect( endpointbpkcountzero ).to.be.within( 60, 75 )
976
+ expect( endpointcpkcountzero ).to.be.within( 60, 75 )
906
977
  expect( endpointapkcountnotzero ).to.be.within( 4, 12 )
907
978
  expect( endpointbpkcountnotzero ).to.be.within( 4, 12 )
908
979
  expect( endpointcpkcountnotzero ).to.be.below( 2 )
@@ -1,4 +1,9 @@
1
1
 
2
+
3
+ //const npl = require( "nodeplotlib" )
4
+ // eslint-disable-next-line no-unused-vars
5
+ const npl = { plot: ( /** @type {any} */ a ) => {} }
6
+
2
7
  /*
3
8
  Note, timing in Node doesn't appear that accurate. This requires more work if
4
9
  we can measure jitter. The pcap traces show 0.01 mS jitter but we are getting 4mS
@@ -8,7 +13,48 @@ when playing in node. For now, leave checking of timing.
8
13
  const expect = require( "chai" ).expect
9
14
  const fs = require( "fs" )
10
15
  const dgram = require( "dgram" )
11
- const prtp = require( "../../index.js" )
16
+ const prtp = require( "../../index" )
17
+ const rtputils = require( "../util/rtp" )
18
+
19
+ function gensignal( hz, datalength, magnitude ) {
20
+
21
+ const y = Buffer.alloc( datalength * 2 )
22
+
23
+ for( let i = 0; i < datalength; i ++ ) {
24
+ y.writeInt16LE( Math.sin( i * ( Math.PI * 2 * ( 1 / 8000 ) ) * hz ) * magnitude, i * 2 )
25
+ }
26
+
27
+ return y
28
+ }
29
+
30
+ /**
31
+ *
32
+ * @param { Array< number > } inarray
33
+ * @returns { Int16Array }
34
+ */
35
+ function pcmutolinear( inarray ) {
36
+ const out = new Int16Array( inarray.length )
37
+ for( let i = 0; i < inarray.length; i++ ) {
38
+ out[ i ] = prtp.projectrtp.codecx.pcmu2linear16( inarray[ i ] )
39
+ }
40
+
41
+ return out
42
+ }
43
+
44
+ /**
45
+ * Limitation of not parsing ccrc.
46
+ * @param { Buffer } packet
47
+ * @return { object }
48
+ */
49
+ function parsertppk( packet ) {
50
+ return {
51
+ sn: packet.readUInt16BE( 2 ),
52
+ ts: packet.readUInt32BE( 4 ),
53
+ pt: packet.readUInt8( 1 ) & 0x7f,
54
+ ssrc: packet.readUInt32BE( 8 ),
55
+ payload: new Uint8Array( packet.slice( 12 ) )
56
+ }
57
+ }
12
58
 
13
59
  /**
14
60
  * @param { number } samples
@@ -75,13 +121,16 @@ describe( "rtpsound", function() {
75
121
  /* create our RTP/UDP endpoint */
76
122
  const server = dgram.createSocket( "udp4" )
77
123
  let receviedpkcount = 0
124
+ let receivedcorrectvalue = 0
78
125
 
79
126
  /** @type { prtp.channel } */
80
127
  let channel
81
128
 
82
129
  server.on( "message", function( msg ) {
83
130
  /* This is PCMA encoded data from our flat file */
84
- expect( msg[ 16 ] ).to.equal( 0x99 )
131
+ const pk = parsertppk( msg )
132
+ /* we can also receive silence */
133
+ if( 228 == pk.payload[ 10 ] ) receivedcorrectvalue++
85
134
  receviedpkcount++
86
135
  } )
87
136
 
@@ -114,10 +163,11 @@ describe( "rtpsound", function() {
114
163
 
115
164
  await finished
116
165
 
117
- expect( receviedpkcount ).to.be.within( 48, 50 )
166
+ expect( receviedpkcount ).to.be.within( 48, 60 )
167
+ expect( receivedcorrectvalue ).to.be.within( 48, 60 )
118
168
  } )
119
169
 
120
- it( "loop in soundsoup and check udp data", function( done ) {
170
+ it( "loop in soundsoup and check udp data", async function() {
121
171
 
122
172
  this.timeout( 6000 )
123
173
  this.slow( 5000 )
@@ -125,45 +175,52 @@ describe( "rtpsound", function() {
125
175
  /* create our RTP/UDP endpoint */
126
176
  const server = dgram.createSocket( "udp4" )
127
177
  let receviedpkcount = 0
128
- let channel
178
+ let correctvalcount = 0
179
+ let done, cleanedup
180
+ const completpromise = new Promise( resolve => done = resolve )
181
+ const cleanedpromise = new Promise( resolve => cleanedup = resolve )
129
182
 
130
183
  server.on( "message", function( msg ) {
131
184
 
132
185
  /* This is PCMA encoded data from our flat file */
133
- expect( msg[ 16 ] ).to.equal( 0x99 )
186
+ if( 228 == msg[ 16 ] ) correctvalcount++
134
187
  receviedpkcount++
135
188
  /*
136
189
  flat.wav has 1 S of audio (8000 samples). 160 per packet compressed = 320 PCM.
137
190
  200 packets is 64000 samples so this must have looped to work.
138
191
  */
139
192
  if( 220 < receviedpkcount ) {
140
- channel.close()
193
+ done()
141
194
  }
142
195
  } )
143
196
 
144
197
  server.bind()
145
- server.on( "listening", async function() {
198
+ await new Promise( resolve => server.on( "listening", resolve ) )
146
199
 
147
- const ourport = server.address().port
200
+ const ourport = server.address().port
148
201
 
149
- channel = await prtp.projectrtp.openchannel( { "remote": { "address": "localhost", "port": ourport, "codec": 0 } }, function( d ) {
202
+ const channel = await prtp.projectrtp.openchannel( { "remote": { "address": "localhost", "port": ourport, "codec": 0 } }, function( d ) {
203
+ if( "close" == d.action ) cleanedup()
204
+ } )
150
205
 
151
- if( "close" === d.action ) {
152
- server.close()
153
- done()
154
- }
155
- } )
206
+ expect( channel.play( {
207
+ "loop": true,
208
+ "files": [
209
+ { "wav": "/tmp/flat.wav" }
210
+ ]
211
+ } ) ).to.be.true
156
212
 
157
- expect( channel.play( {
158
- "loop": true,
159
- "files": [
160
- { "wav": "/tmp/flat.wav" }
161
- ]
162
- } ) ).to.be.true
213
+ await completpromise
163
214
 
164
- } )
215
+ channel.close()
216
+ server.close()
217
+
218
+ await cleanedpromise
219
+ expect( correctvalcount ).to.be.above( 210 )
165
220
  } )
166
221
 
222
+ /* I have also used this test to play with file codec conversion to check it is working properly */
223
+
167
224
  it( "loop in soundsoup file and check udp data", function( done ) {
168
225
 
169
226
  this.timeout( 6000 )
@@ -174,10 +231,12 @@ describe( "rtpsound", function() {
174
231
  let receviedpkcount = 0
175
232
  let channel
176
233
 
234
+ let receivedpcmu = []
177
235
  server.on( "message", function( msg ) {
236
+ receivedpcmu = [ ...receivedpcmu, ...Array.from( pcmutolinear( rtputils.parsepk( msg ).payload ) ) ]
178
237
 
179
238
  /* This is PCMA encoded data from our flat file */
180
- expect( msg[ 16 ] ).to.equal( 0x99 )
239
+ //expect( msg[ 16 ] ).to.equal( 0x99 )
181
240
 
182
241
  receviedpkcount++
183
242
  /*
@@ -199,13 +258,20 @@ describe( "rtpsound", function() {
199
258
 
200
259
  if( "close" === d.action ) {
201
260
  server.close()
261
+
262
+ npl.plot( [ {
263
+ y: receivedpcmu,
264
+ type: "scatter"
265
+ } ] )
266
+
267
+
202
268
  done()
203
269
  }
204
270
  } )
205
271
 
206
272
  expect( channel.play( {
207
273
  "files": [
208
- { "loop": true, "wav": "/tmp/flat.wav" }
274
+ { "loop": true, "wav": "/tmp/440sine.wav" }
209
275
  ]
210
276
  } ) ).to.be.true
211
277
 
@@ -220,6 +286,7 @@ describe( "rtpsound", function() {
220
286
  /* create our RTP/UDP endpoint */
221
287
  const server = dgram.createSocket( "udp4" )
222
288
  let receviedpkcount = 0
289
+ let receviedgoodcount = 0
223
290
 
224
291
  /** @type { prtp.channel } */
225
292
  let channel
@@ -232,8 +299,7 @@ describe( "rtpsound", function() {
232
299
  receviedpkcount++
233
300
 
234
301
  /* This is PCMA encoded data from our flat file */
235
- expect( msg[ 17 ] ).to.equal( 153 /* 0x99 */ )
236
- expect( msg[ 150 ] ).to.equal( 153 /* 0x99 */ )
302
+ if( 228 == msg[ 17 ] ) receviedgoodcount++
237
303
  } )
238
304
 
239
305
  server.bind()
@@ -258,7 +324,8 @@ describe( "rtpsound", function() {
258
324
  8000 samples looped twice. 100 packets (50 packets/S).
259
325
  */
260
326
 
261
- expect( receviedpkcount ).to.be.within( 97, 100 )
327
+ expect( receviedpkcount ).to.above( 90 )
328
+ expect( receviedgoodcount ).to.be.above( 90 )
262
329
  } )
263
330
 
264
331
  it( "slightly more complex soundsoup file and check udp data", async function() {
@@ -270,73 +337,61 @@ describe( "rtpsound", function() {
270
337
  const server = dgram.createSocket( "udp4" )
271
338
  let receviedpkcount = 0
272
339
 
273
- /** @type { prtp.channel } */
274
- let channel
340
+ let enoughpackets
341
+ const enoughpacketspromise = new Promise( resolve => enoughpackets = resolve )
275
342
 
276
343
  let done
277
344
  const finished = new Promise( ( r ) => { done = r } )
278
345
 
346
+ // eslint-disable-next-line complexity
279
347
  server.on( "message", function( msg ) {
280
348
 
281
349
  receviedpkcount++
282
-
283
- /* This is PCMA encoded data from our soundsoup:
284
- { "loop": 2, "files": [
285
- { "wav": "/tmp/flat.wav", "loop": 2 },
286
- { "wav": "/tmp/flat2.wav" },
287
- { "wav": "/tmp/flat.wav" },
288
- { "wav": "/tmp/flat3.wav", "start": 40, "stop": 60 }, should be 2 packets
289
- ] }
290
- */
291
- if( 75 == receviedpkcount ) {
292
- /* flat.wav */
293
- expect( msg[ 17 ] ).to.equal( 153 /* 0x99 */ )
294
- expect( msg[ 150 ] ).to.equal( 153 /* 0x99 */ )
295
- } else if( 125 == receviedpkcount ) {
296
- /* flat2.wav */
297
- expect( msg[ 17 ] ).to.equal( 3 )
298
- expect( msg[ 150 ] ).to.equal( 3 )
299
- } else if( 175 == receviedpkcount ) {
300
- /* flat.wav */
301
- expect( msg[ 17 ] ).to.equal( 153 /* 0x99 */ )
302
- expect( msg[ 150 ] ).to.equal( 153 /* 0x99 */ )
303
- } else if( 250 == receviedpkcount ) {
304
- /* flat.wav */
305
- expect( msg[ 17 ] ).to.equal( 153 )
306
- expect( msg[ 150 ] ).to.equal( 153 )
307
- } else if( 325 == receviedpkcount ) {
350
+ const secondouterloopoffset = 205
351
+
352
+ /* This is PCMA encoded data from our soundsoup */
353
+ if( 25 == receviedpkcount || ( 25 + secondouterloopoffset ) == receviedpkcount ) {
354
+ /* flat.wav first loop */
355
+ expect( msg[ 17 ] ).to.equal( 228 )
356
+ expect( msg[ 150 ] ).to.equal( 228 )
357
+ } else if( 75 == receviedpkcount || ( 75 + secondouterloopoffset ) == receviedpkcount ) {
358
+ /* flat.wav second loop */
359
+ expect( msg[ 17 ] ).to.equal( 228 )
360
+ expect( msg[ 150 ] ).to.equal( 228 )
361
+ } else if( 125 == receviedpkcount || ( 125 + secondouterloopoffset ) == receviedpkcount ) {
308
362
  /* flat2.wav */
309
- expect( msg[ 17 ] ).to.equal( 3 )
310
- expect( msg[ 150 ] ).to.equal( 3 )
311
- } else if( 375 == receviedpkcount ) {
363
+ expect( msg[ 17 ] ).to.equal( 223 )
364
+ expect( msg[ 150 ] ).to.equal( 223 )
365
+ } else if( 175 == receviedpkcount || ( 175 + secondouterloopoffset ) == receviedpkcount ) {
312
366
  /* flat.wav */
313
- expect( msg[ 17 ] ).to.equal( 153 )
314
- expect( msg[ 150 ] ).to.equal( 153 )
315
- } else if( 403 == receviedpkcount ) {
367
+ expect( msg[ 17 ] ).to.equal( 228 )
368
+ expect( msg[ 150 ] ).to.equal( 228 )
369
+ } else if( 206 == receviedpkcount || 413 == receviedpkcount ) {
316
370
  /* flat3.wav */
317
- expect( msg[ 17 ] ).to.equal( 54 )
318
- expect( msg[ 150 ] ).to.equal( 54 )
371
+ expect( msg[ 17 ] ).to.equal( 220 )
372
+ expect( msg[ 150 ] ).to.equal( 220 )
373
+ } else if( ( 207 * 2 ) == receviedpkcount ) {
374
+ enoughpackets()
319
375
  }
320
376
  } )
321
377
 
322
378
  server.bind()
323
- server.on( "listening", async function() {
324
-
325
- const ourport = server.address().port
379
+ await new Promise( resolve => server.on( "listening", resolve ) )
326
380
 
327
- channel = await prtp.projectrtp.openchannel( { "remote": { "address": "localhost", "port": ourport, "codec": 0 } }, function( d ) {
328
- if( "close" === d.action ) done()
329
- } )
381
+ const ourport = server.address().port
330
382
 
331
- expect( channel.play( { "loop": 2, "files": [
332
- { "wav": "/tmp/flat.wav", "loop": 2 },
333
- { "wav": "/tmp/flat2.wav" },
334
- { "wav": "/tmp/flat.wav" },
335
- { "wav": "/tmp/flat3.wav", "start": 40, "stop": 60 }, /* should be 2 packets */
336
- ] } ) ).to.be.true
383
+ const channel = await prtp.projectrtp.openchannel( { "remote": { "address": "localhost", "port": ourport, "codec": 0 } }, function( d ) {
384
+ if( "close" === d.action ) done()
337
385
  } )
338
386
 
339
- await new Promise( ( resolve ) => { setTimeout( () => resolve(), 9000 ) } )
387
+ expect( channel.play( { "loop": 2, "files": [
388
+ { "wav": "/tmp/flat.wav", "loop": 2 },
389
+ { "wav": "/tmp/flat2.wav" },
390
+ { "wav": "/tmp/flat.wav" },
391
+ { "wav": "/tmp/flat3.wav", "start": 40, "stop": 100 }, /* should be 4 packets */
392
+ ] } ) ).to.be.true
393
+
394
+ await enoughpacketspromise
340
395
  channel.close()
341
396
  server.close()
342
397
 
@@ -345,7 +400,7 @@ describe( "rtpsound", function() {
345
400
  /*
346
401
  8000 samples looped twice with 3 sections to play. 400 packets (50 packets/S).
347
402
  */
348
- expect( receviedpkcount ).to.be.within( 390, 405 )
403
+ expect( receviedpkcount ).to.be.above( 400 )
349
404
  } )
350
405
 
351
406
  before( async () => {
@@ -355,35 +410,34 @@ describe( "rtpsound", function() {
355
410
  const wavheader = createwavheader( samples )
356
411
  const values = Buffer.alloc( samples * bytespersample )
357
412
  for( let i = 0; i < samples; i++ ) {
358
- values.writeUInt16BE( 300, i * 2 )
413
+ values.writeUInt16LE( 300, i * 2 )
359
414
  }
360
415
  /* Put a marker at the start of the file */
361
- values.writeUInt16BE( 1000, 50 )
416
+ values.writeUInt16LE( 1000, 50 )
362
417
 
363
- await fs.writeFile( "/tmp/flat.wav", Buffer.concat( [ wavheader, values ] ), function() {} )
418
+ await fs.promises.writeFile( "/tmp/flat.wav", Buffer.concat( [ wavheader, values ] ) )
364
419
 
365
420
  for( let i = 0; i < samples; i++ ) {
366
- values.writeUInt16BE( 400, i * 2 )
421
+ values.writeUInt16LE( 400, i * 2 )
367
422
  }
368
423
 
369
- await fs.writeFile( "/tmp/flat2.wav", Buffer.concat( [ wavheader, values ] ), function() {} )
424
+ await fs.promises.writeFile( "/tmp/flat2.wav", Buffer.concat( [ wavheader, values ] ) )
370
425
 
371
426
  for( let i = 0; i < samples; i++ ) {
372
- values.writeUInt16BE( 0, i * 2 )
427
+ values.writeUInt16LE( 500, i * 2 )
373
428
  }
374
429
 
430
+ await fs.promises.writeFile( "/tmp/flat3.wav", Buffer.concat( [ wavheader, values ] ) )
375
431
 
376
- for( let i = 0; 320 > i; i++ ) {
377
- values.writeUInt16BE( 500, 640 + ( i * 2 ) )
378
- }
379
-
380
- await fs.writeFile( "/tmp/flat3.wav", Buffer.concat( [ wavheader, values ] ), function() {} )
432
+ const sig = gensignal( 440, 8000, 1000 )
433
+ await fs.promises.writeFile( "/tmp/440sine.wav", Buffer.concat( [ wavheader, sig ] ) )
381
434
 
382
435
  } )
383
436
 
384
437
  after( async () => {
385
- await new Promise( ( resolve ) => { fs.unlink( "/tmp/flat.wav", () => { resolve() } ) } )
386
- await new Promise( ( resolve ) => { fs.unlink( "/tmp/flat2.wav", () => { resolve() } ) } )
387
- await new Promise( ( resolve ) => { fs.unlink( "/tmp/flat3.wav", () => { resolve() } ) } )
438
+ await fs.promises.unlink( "/tmp/flat.wav" )
439
+ await fs.promises.unlink( "/tmp/flat2.wav" )
440
+ await fs.promises.unlink( "/tmp/flat3.wav" )
441
+ await fs.promises.unlink( "/tmp/440sine.wav" )
388
442
  } )
389
443
  } )
@@ -1,7 +1,7 @@
1
1
  /* if we want to see what is going on - use nodeplotlib instead of our placeholder */
2
- const npl = require( "nodeplotlib" )
2
+ //const npl = require( "nodeplotlib" )
3
3
  // eslint-disable-next-line no-unused-vars
4
- //const npl = { plot: ( /** @type {any} */ a ) => {} }
4
+ const npl = { plot: ( /** @type {any} */ a ) => {} }
5
5
 
6
6
 
7
7
  const fft = require( "fft-js" ).fft
@@ -0,0 +1,17 @@
1
+
2
+
3
+
4
+ /**
5
+ * Limitation of not parsing ccrc.
6
+ * @param { Buffer } packet
7
+ * @return { object }
8
+ */
9
+ module.exports.parsepk = function( packet ) {
10
+ return {
11
+ sn: packet.readUInt16BE( 2 ),
12
+ ts: packet.readUInt32BE( 4 ),
13
+ pt: packet.readUInt8( 1 ) & 0x7f,
14
+ ssrc: packet.readUInt32BE( 8 ),
15
+ payload: new Uint8Array( packet.slice( 12 ) )
16
+ }
17
+ }