recsf.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  1. /*
  2. * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
  3. * http://people.bath.ac.uk/masrwd
  4. * http://www.composersdesktop.com
  5. * This file is part of the CDP System.
  6. * The CDP System is free software; you can redistribute it
  7. * and/or modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * The CDP System is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. * See the GNU Lesser General Public License for more details.
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with the CDP System; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. *
  19. */
  20. /*
  21. * recsf.c
  22. *
  23. */
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <memory.h>
  27. #include <math.h>
  28. #include <assert.h>
  29. #include <time.h>
  30. #include <string.h>
  31. #ifdef _WIN32
  32. #define _WIN32_WINNT 0x0500
  33. #include <conio.h>
  34. #include <windows.h>
  35. #include <process.h>
  36. // NB: need to have PAWIN_USE_WDMKS_DEVICE_INFO defined
  37. #include "pa_win_ds.h"
  38. #endif
  39. #ifdef unix
  40. #include <sys/types.h>
  41. #include <sys/timeb.h>
  42. #endif
  43. #include <signal.h>
  44. #if defined unix || defined linux
  45. #include <aaio.h>
  46. #endif
  47. #ifdef MAC
  48. #include <libkern/OSAtomic.h>
  49. #endif
  50. #ifdef unix
  51. #include <sys/time.h>
  52. #include <pthread.h>
  53. /* in portsf.lib */
  54. extern int stricmp(const char *a, const char *b);
  55. #endif
  56. #include "portaudio.h"
  57. #include "pa_ringbuffer.h"
  58. #include "pa_util.h"
  59. #include "portsf.h"
  60. #ifndef min
  61. #define min(x,y) ((x) < (y) ? (x) : (y))
  62. #endif
  63. #define N_BFORMATS (10)
  64. static const int bformats[N_BFORMATS] = {2,3,4,5,6,7,8,9,11,16};
  65. //#define FRAMES_PER_BUFFER (4096)
  66. // want nice big buffer for recording!
  67. #define RINGBUF_NFRAMES (32768)
  68. #define NUM_WRITES_PER_BUFFER (4)
  69. #define DEFAULT_SRATE (44100)
  70. #define DEFAULT_STYPE (1)
  71. #define DEFAULT_CHANS (2)
  72. #define DBRANGE (64)
  73. enum { ARG_PROGNAME, ARG_OUTFILE, ARG_DUR };
  74. #ifdef _WIN32
  75. HANDLE ghEvent;
  76. #endif
  77. typedef struct {
  78. PaUtilRingBuffer ringbuf;
  79. PaStream *stream;
  80. float *ringbufData;
  81. char peakstr[(DBRANGE/2)+1];
  82. PaTime startTime;
  83. PaTime lastTime;
  84. #ifdef WIN32
  85. void *hThread;
  86. HANDLE hTimer;
  87. HANDLE hTimerCount;
  88. #endif
  89. #ifdef unix
  90. pthread_t hThread;
  91. #endif
  92. double peak;
  93. unsigned long frames_written;
  94. unsigned long frames_to_write; // for optional duration arg
  95. unsigned long current_frame;
  96. int srate;
  97. int chans;
  98. int flag;
  99. int ofd;
  100. int finished;
  101. int showlevels;
  102. } psfdata;
  103. static int file_recording;
  104. static psfdata *g_pdata = NULL; // for timer interrupt routine
  105. int show_devices(void);
  106. void playhandler(int sig)
  107. {
  108. if(sig == SIGINT)
  109. file_recording = 0;
  110. }
  111. #ifdef unix
  112. void alarm_wakeup (int i)
  113. {
  114. struct itimerval tout_val;
  115. signal(SIGALRM,alarm_wakeup);
  116. if(file_recording && g_pdata->stream) {
  117. double dBpeak = (int)( 20.0 * log10(sqrt(g_pdata->peak)));
  118. int dBmin = -DBRANGE;
  119. int i;
  120. for(i=0;i < DBRANGE/2;i++){
  121. g_pdata->peakstr[i] = dBpeak > dBmin+(i*2) ? '*' : '.';
  122. }
  123. //printf("%.4f secs\r",(float)(g_pdata->frames_played /(double) g_pdata->srate));
  124. g_pdata->lastTime = Pa_GetStreamTime(g_pdata->stream ) - g_pdata->startTime;
  125. //printf("%.2f secs\r", g_pdata->lastTime);
  126. if(g_pdata->showlevels)
  127. printf("%.2f secs\t\t\%s\r", g_pdata->lastTime,g_pdata->peakstr );
  128. else
  129. printf("%.2f secs\r", g_pdata->lastTime);
  130. fflush(stdout);
  131. }
  132. tout_val.it_interval.tv_sec = 0;
  133. tout_val.it_interval.tv_usec = 0;
  134. tout_val.it_value.tv_sec = 0;
  135. tout_val.it_value.tv_usec = 250000;
  136. setitimer(ITIMER_REAL, &tout_val,0);
  137. }
  138. #endif
  139. void finishedCallback(void *userData)
  140. {
  141. psfdata *pdata = (psfdata*) userData;
  142. //printf("stream finished!\n");
  143. pdata->finished = 1;
  144. file_recording = 0;
  145. }
  146. #ifdef WIN32
  147. VOID CALLBACK TimerCallback(PVOID lpParam, BOOLEAN TimerOrWaitFired)
  148. {
  149. psfdata *pdata = (psfdata*) lpParam;
  150. if(file_recording && pdata->stream) {
  151. //printf("%.4f secs\r",(float)(g_pdata->frames_played /(double) g_pdata->srate));
  152. double dBpeak = (int)( 20.0 * log10(sqrt(pdata->peak)));
  153. int dBmin = -DBRANGE;
  154. int dBval = dBmin;
  155. int i;
  156. for(i=0;i < DBRANGE/2;i++) {
  157. pdata->peakstr[i] = dBpeak > dBmin+(i*2) ? '*' : '.';
  158. }
  159. pdata->lastTime = Pa_GetStreamTime(pdata->stream ) - pdata->startTime;
  160. printf("%.2f secs\t\t%s\r", pdata->lastTime,pdata->peakstr );
  161. fflush(stdout);
  162. SetEvent(ghEvent);
  163. }
  164. else
  165. printf("\n");
  166. }
  167. #endif
  168. // TODO: implement optional duration arg
  169. #ifdef unix
  170. static int threadFunctionWriteFile(void* ptr)
  171. #else
  172. static unsigned int __stdcall threadFunctionWriteFile(void* ptr)
  173. #endif
  174. {
  175. psfdata* pData = (psfdata*)ptr;
  176. /* Mark thread started */
  177. pData->flag = 0;
  178. while (1) {
  179. ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferReadAvailable(&pData->ringbuf);
  180. if(file_recording == 0){
  181. //write out whatever remains in ring buffer
  182. void* ptr[2] = {0};
  183. ring_buffer_size_t sizes[2] = {0};
  184. //printf("flushing ring buffer...\n");
  185. ring_buffer_size_t elementsRead = PaUtil_GetRingBufferReadRegions(&pData->ringbuf, elementsInBuffer, ptr + 0, sizes + 0, ptr + 1, sizes + 1);
  186. if (elementsRead > 0) {
  187. int i;
  188. for (i = 0; i < 2 && ptr[i] != NULL; ++i) {
  189. unsigned long towrite = sizes[i];
  190. if(pData->frames_to_write){
  191. if(pData->frames_written + towrite > pData->frames_to_write)
  192. towrite = pData->frames_to_write - pData->frames_written;
  193. }
  194. if(psf_sndWriteFloatFrames(pData->ofd,(float*) ptr[i],towrite) != towrite) {
  195. printf("File %d write error\n",pData->ofd);
  196. pData->flag = 0;
  197. break;
  198. }
  199. pData->frames_written += towrite;
  200. if(pData->frames_written == pData->frames_to_write){
  201. //printf("recording done\n");
  202. pData->flag = 1;
  203. file_recording = 0;
  204. break;
  205. }
  206. }
  207. PaUtil_AdvanceRingBufferReadIndex(&pData->ringbuf, elementsRead);
  208. }
  209. break;
  210. }
  211. if ( (elementsInBuffer >= pData->ringbuf.bufferSize / NUM_WRITES_PER_BUFFER) || pData->flag ) {
  212. void* ptr[2] = {0};
  213. ring_buffer_size_t sizes[2] = {0};
  214. /* By using PaUtil_GetRingBufferReadRegions, we can read directly from the ring buffer */
  215. ring_buffer_size_t elementsRead = PaUtil_GetRingBufferReadRegions(&pData->ringbuf, elementsInBuffer, ptr + 0, sizes + 0, ptr + 1, sizes + 1);
  216. if (elementsRead > 0) {
  217. int i;
  218. for (i = 0; i < 2 && ptr[i] != NULL; ++i) {
  219. unsigned long towrite = sizes[i];
  220. if(pData->frames_to_write){
  221. if(pData->frames_written + towrite > pData->frames_to_write)
  222. towrite = pData->frames_to_write - pData->frames_written;
  223. }
  224. if(psf_sndWriteFloatFrames(pData->ofd,(float*) ptr[i],towrite) != towrite) {
  225. printf("File %d write error\n",pData->ofd);
  226. pData->flag = 0;
  227. break;
  228. }
  229. pData->frames_written += towrite;
  230. if(pData->frames_written == pData->frames_to_write){
  231. //printf("recording done\n");
  232. pData->flag = 1;
  233. file_recording = 0;
  234. break;
  235. }
  236. }
  237. PaUtil_AdvanceRingBufferReadIndex(&pData->ringbuf, elementsRead);
  238. }
  239. if (pData->flag) {
  240. break;
  241. }
  242. }
  243. /* Sleep a little while... */
  244. Pa_Sleep(10);
  245. }
  246. return 0;
  247. }
  248. #ifdef unix
  249. // TODO: unix return type should be void*
  250. typedef int (*threadfunc)(void*);
  251. #endif
  252. #ifdef WIN32
  253. typedef unsigned int (__stdcall *threadfunc)(void*);
  254. #endif
  255. /* Start up a new thread for given function */
  256. static PaError startThread( psfdata* pdata, threadfunc fn )
  257. {
  258. pdata->flag = 1;
  259. #ifdef _WIN32
  260. pdata->hThread = (void*)_beginthreadex(NULL, 0, fn, pdata, 0, NULL);
  261. if (pdata->hThread == NULL)
  262. return paUnanticipatedHostError;
  263. /* Wait for thread to startup */
  264. while (pdata->flag) {
  265. Pa_Sleep(10);
  266. }
  267. /* Set file thread to a little higher prio than normal */
  268. SetThreadPriority(pdata->hThread, THREAD_PRIORITY_ABOVE_NORMAL);
  269. #else
  270. #if defined(__APPLE__) || defined(__GNUC__)
  271. if(pthread_create(&pdata->hThread,NULL,(void*) fn,pdata))
  272. return -1;
  273. /* Wait for thread to startup */
  274. while (pdata->flag) {
  275. Pa_Sleep(10);
  276. }
  277. #endif
  278. #endif
  279. return paNoError;
  280. }
  281. // for sake of completion - curently NOT USED
  282. #if 0
  283. static int stopThread( psfdata* pdata )
  284. {
  285. // RWD: just called when all data played; must be called before StopStream
  286. // pdata->flag = 1;
  287. /* Wait for thread to stop */
  288. while (pdata->flag == 0) {
  289. Pa_Sleep(10);
  290. }
  291. #ifdef _WIN32
  292. CloseHandle(pdata->hThread);
  293. pdata->hThread = 0;
  294. #else
  295. #if defined(__APPLE__) || defined(__GNUC__)
  296. pthread_cancel(pdata->hThread);
  297. #endif
  298. #endif
  299. return paNoError;
  300. }
  301. #endif
  302. static int recordCallback( const void *inputBuffer, void *outputBuffer,
  303. unsigned long framesPerBuffer,
  304. const PaStreamCallbackTimeInfo* timeInfo,
  305. PaStreamCallbackFlags statusFlags,
  306. void *userData )
  307. {
  308. psfdata *data = (psfdata*) userData;
  309. const float *rptr = (const float*) inputBuffer;
  310. double peak = 0.0, val;
  311. unsigned int i;
  312. (void) outputBuffer; /* Prevent unused variable warnings. */
  313. (void) timeInfo;
  314. (void) statusFlags;
  315. (void) userData;
  316. data->current_frame += PaUtil_WriteRingBuffer(&data->ringbuf, rptr, framesPerBuffer);
  317. // simple level meter!
  318. val = rptr[0];
  319. val = val * val;
  320. peak = val;
  321. for(i=0;i < framesPerBuffer * data->chans ;i++){
  322. val = rptr[i];
  323. val = val * val;
  324. peak = peak < val ? val : peak;
  325. }
  326. data->peak = peak;
  327. if(data->flag) {
  328. return paComplete; // or paAbort?
  329. }
  330. return paContinue;
  331. }
  332. void usage(void)
  333. {
  334. printf("usage: recsf [-BN][-cN][-dN][-hN][-i][-p][-rN][-tN] outfile [dur]\n"
  335. "outfile: output file in WAVE,AIFF or AIFC formats,\n"
  336. " determined by the file extension.\n"
  337. " use extension .amb to create a B-Format file.\n"
  338. "-BN : set memory buffer size to N frames (default: %d).\n"
  339. " N must be a power of 2 (e.g 4096, 8192, 16384 etc).\n"
  340. "-cN : set channels to N (default: 2).\n"
  341. "-hN : set hardware buffer size to N frames (default: set by device)\n"
  342. "-p : suppress running peak level indicator\n"
  343. "-rN : set sample rate to N Hz (default: 44100)\n"
  344. "-tN : set sample type to N (default: 1)\n"
  345. " 0 : 16 bits\n"
  346. " 1 : 24 bits\n"
  347. " 2 : 32bit floats\n"
  348. "dur : optional fixed duration for outfile\n"
  349. " (overriden by Ctrl-C)\n"
  350. " Otherwise, use Ctrl_C to terminate recording.\n"
  351. "-dN : use input device N\n"
  352. "-i : start recording immediately (default: wait for keypress)\n"
  353. ,RINGBUF_NFRAMES);
  354. }
  355. static unsigned NextPowerOf2(unsigned val)
  356. {
  357. val--;
  358. val = (val >> 1) | val;
  359. val = (val >> 2) | val;
  360. val = (val >> 4) | val;
  361. val = (val >> 8) | val;
  362. val = (val >> 16) | val;
  363. return ++val;
  364. }
  365. int main(int argc,char **argv)
  366. {
  367. PaStreamParameters inputParameters;
  368. #ifdef _WIN32
  369. /* portaudio sets default channel masks we can't use; we must do all this to set default mask = 0! */
  370. PaWinDirectSoundStreamInfo directSoundStreamInfo;
  371. // PaWinMmeStreamInfo winmmeStreamInfo;
  372. #endif
  373. PaDeviceInfo *devinfo = NULL;
  374. PaStream *stream = NULL;
  375. // PaStreamCallback *callback = recordCallback;
  376. PaError err = paNoError;
  377. psfdata sfdata;
  378. PSF_CHPEAK *fpeaks = NULL;
  379. PSF_PROPS props;
  380. const char* outfilename = NULL;
  381. MYLONG lpeaktime;
  382. int waitkey = 1;
  383. int showlevels = 1;
  384. double duration = 0.0;
  385. int i;
  386. int res;
  387. int samptype = DEFAULT_STYPE;
  388. char* ext = NULL;
  389. PaDeviceIndex device;
  390. unsigned long ringframelen = RINGBUF_NFRAMES; // length of ring buffer in m/c frames
  391. unsigned long frames_per_buffer = 0;
  392. unsigned int frameBlocksize = 0;
  393. double maxdurFrames = 0.0;
  394. unsigned long LmaxdurFrames = 0;
  395. unsigned long LmaxdurSecs = 0;
  396. unsigned long LmaxdurMins = 0;
  397. #ifdef unix
  398. struct itimerval tout_val;
  399. tout_val.it_interval.tv_sec = 0;
  400. tout_val.it_interval.tv_usec = 0;
  401. tout_val.it_value.tv_sec = 0;
  402. tout_val.it_value.tv_usec = 200000;
  403. #endif
  404. #ifdef _WIN32
  405. HANDLE hTimerQueue;
  406. ghEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
  407. if(ghEvent==NULL){
  408. printf("Failed to start internal timer (1).\n");
  409. return 1;
  410. }
  411. hTimerQueue = CreateTimerQueue();
  412. if(hTimerQueue == NULL){
  413. printf("Failed to start internal timer (2).\n");
  414. return 1;
  415. }
  416. #endif
  417. props.chans = DEFAULT_CHANS;
  418. props.srate = DEFAULT_SRATE;
  419. props.samptype = samptype;
  420. signal(SIGINT,playhandler);
  421. sfdata.ringbufData = NULL;
  422. sfdata.frames_to_write = 0;
  423. printf("RECSF: multi-channel record to file. v 1.1.0 RWD,CDP 2013\n");
  424. file_recording = 0;
  425. err = Pa_Initialize();
  426. if( err != paNoError ) {
  427. printf("Failed to initialize Portaudio.\n");
  428. Pa_Terminate();
  429. return 1;
  430. }
  431. device = Pa_GetDefaultInputDevice();
  432. /* CDP version number */
  433. if(argc==2 && (stricmp(argv[1],"--version")==0)){
  434. printf("1.1.0\n");
  435. return 0;
  436. }
  437. if(argc < ARG_DUR) {
  438. printf("insufficient args\n");
  439. usage();
  440. show_devices();
  441. Pa_Terminate();
  442. return 1;
  443. }
  444. while(argv[1][0]=='-'){
  445. int err = 0;
  446. unsigned long userbuflen = 0;
  447. switch(argv[1][1]){
  448. case 'c':
  449. if(argv[1][2]=='\0'){
  450. printf("-c flag requires parameter\n");
  451. err++;
  452. break;
  453. }
  454. props.chans = atoi(&(argv[1][2]));
  455. if(props.chans < 1){
  456. printf("bad value %d for channels\n",props.chans);
  457. err++;
  458. }
  459. break;
  460. case 'd':
  461. if(argv[1][2]=='\0'){
  462. printf("-d flag requires parameter\n");
  463. err++;
  464. break;
  465. }
  466. device = atoi(&(argv[1][2]));
  467. break;
  468. case 'h':
  469. if(argv[1][2]=='\0'){
  470. printf("-h flag requires parameter\n");
  471. err++;
  472. break;
  473. }
  474. frames_per_buffer = atoi(&(argv[1][2]));
  475. if(frames_per_buffer < 32){
  476. printf("-h value too small; must be >= 32\n");
  477. err++;
  478. }
  479. break;
  480. case 'i':
  481. waitkey = 0;
  482. break;
  483. case 'p':
  484. showlevels = 0;
  485. break;
  486. case 'r':
  487. if(argv[1][2]=='\0'){
  488. printf("-r flag requires parameter\n");
  489. err++;
  490. break;
  491. }
  492. props.srate = atoi(&(argv[1][2]));
  493. if(props.srate <= 0){
  494. printf("bad value %d for srate\n",props.srate);
  495. err++;
  496. }
  497. break;
  498. case 't':
  499. if(argv[1][2]=='\0'){
  500. printf("-t flag requires parameter\n");
  501. err++;
  502. break;
  503. }
  504. samptype = atoi(&(argv[1][2]));
  505. if(samptype < 0 || samptype > 2){
  506. printf("bad value %d for sample type\n",samptype);
  507. err++;
  508. }
  509. break;
  510. case 'B':
  511. if(argv[1][2]=='\0'){
  512. printf("-B flag requires parameter\n");
  513. err++;
  514. break;
  515. }
  516. ringframelen = atoi(&(argv[1][2]));
  517. if(ringframelen < 1024){
  518. printf("-B: buffer size must be >=1024\n");
  519. err++;
  520. }
  521. userbuflen = NextPowerOf2(ringframelen);
  522. if(userbuflen != ringframelen){
  523. printf("-B: buffer size must be power of 2 size\n");
  524. Pa_Terminate();
  525. return 1;
  526. }
  527. break;
  528. default:
  529. printf("unrecognised flag option\n");
  530. err++;
  531. break;
  532. }
  533. if(err){
  534. Pa_Terminate();
  535. return 1;
  536. }
  537. argv++; argc--;
  538. }
  539. if(argc < ARG_DUR || argc > ARG_DUR+1) {
  540. usage();
  541. show_devices();
  542. Pa_Terminate();
  543. return 1;
  544. }
  545. outfilename = argv[ARG_OUTFILE];
  546. switch(samptype){
  547. case 0:
  548. props.samptype = PSF_SAMP_16;
  549. frameBlocksize = 2;
  550. break;
  551. case 1:
  552. props.samptype = PSF_SAMP_24;
  553. frameBlocksize = 3;
  554. break;
  555. case 2:
  556. props.samptype = PSF_SAMP_IEEE_FLOAT;
  557. frameBlocksize = 4;
  558. break;
  559. default:
  560. printf("stype must be 0,1 or 2\n");
  561. Pa_Terminate();
  562. return 1;
  563. }
  564. // find max recording time, with safety margin
  565. frameBlocksize *= props.chans;
  566. maxdurFrames = (pow(2.0,32.0) / frameBlocksize) - 1000;
  567. LmaxdurFrames = (unsigned long) maxdurFrames;
  568. LmaxdurSecs = LmaxdurFrames / props.srate;
  569. LmaxdurMins = LmaxdurSecs / 60;
  570. printf("Max recording time: %lu mins, %lu secs\n",LmaxdurMins,LmaxdurSecs - (LmaxdurMins * 60));
  571. if(argc==ARG_DUR+1){
  572. duration = atof(argv[ARG_DUR]);
  573. if(duration <=0.0){
  574. printf("duration must be positive!\n");
  575. Pa_Terminate();
  576. return 1;
  577. }
  578. if(duration > (double) LmaxdurSecs){
  579. printf("specified duration too long for file format.\n");
  580. Pa_Terminate();
  581. return 1;
  582. }
  583. sfdata.frames_to_write = (unsigned long) (duration * props.srate);
  584. }
  585. ext = strrchr(outfilename,'.');
  586. if(ext && stricmp(ext,".amb")==0) {
  587. int matched = 0;
  588. for(i=0;i < N_BFORMATS;i++) {
  589. if(props.chans == bformats[i]){
  590. matched = 1;
  591. break;
  592. }
  593. }
  594. if(!matched){
  595. printf("WARNING: No Bformat definition for %d-channel file.\n",props.chans);
  596. }
  597. props.format = PSF_WAVE_EX;
  598. props.chformat = MC_BFMT;
  599. }
  600. else {
  601. // we must be strictly correct with WAVE formats!
  602. props.chformat = props.chans > 2 ? MC_STD : STDWAVE;
  603. props.format = psf_getFormatExt(outfilename);
  604. if(props.chans > 2 || props.samptype > PSF_SAMP_16 || props.srate > 48000)
  605. props.format = PSF_WAVE_EX;
  606. }
  607. sfdata.ofd = psf_sndCreate(outfilename,&props,0,0,PSF_CREATE_RDWR);
  608. if(sfdata.ofd < 0){
  609. printf("Sorry - unable to create outfile\n");
  610. goto error;
  611. }
  612. fpeaks = (PSF_CHPEAK *) calloc(props.chans,sizeof(PSF_CHPEAK));
  613. if(fpeaks==NULL){
  614. puts("no memory for PEAK data\n");
  615. goto error;
  616. }
  617. if(props.srate > 48000)
  618. ringframelen <<= 1;
  619. printf("File buffer size = %ld\n",ringframelen);
  620. // NB ring buffer sized for decoded data, hence outchans here; otherwise inchans = outchans
  621. sfdata.ringbufData = (float *) PaUtil_AllocateMemory( ringframelen * sizeof(float) * props.chans); /* From now on, recordedSamples is initialised. */
  622. if( sfdata.ringbufData == NULL ) {
  623. puts("Could not allocate play buffer.\n");
  624. goto error;
  625. }
  626. // number of elements has to be a power of two, so each element has to be a full m/c frame
  627. if (PaUtil_InitializeRingBuffer(&sfdata.ringbuf, sizeof(float) * props.chans, ringframelen , sfdata.ringbufData) < 0) {
  628. puts("Could not initialise play buffer.\n");
  629. goto error;
  630. }
  631. // scale frame buf to sample rate
  632. if(props.srate > 48000)
  633. frames_per_buffer *= 2;
  634. if(frames_per_buffer > 0)
  635. printf("Audio buffer size = %lu frames\n",frames_per_buffer);
  636. sfdata.chans = props.chans;
  637. sfdata.frames_written = 0;
  638. sfdata.current_frame = 0;
  639. sfdata.srate = props.srate;
  640. sfdata.peakstr[DBRANGE/2] = '\0';
  641. sfdata.finished = 0;
  642. sfdata.showlevels = showlevels;
  643. g_pdata = &sfdata;
  644. inputParameters.device = device; /*Pa_GetDefaultOutputDevice(); */ /* default output device */
  645. inputParameters.channelCount = props.chans;
  646. inputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
  647. inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency;
  648. inputParameters.hostApiSpecificStreamInfo = NULL;
  649. devinfo = (PaDeviceInfo *) Pa_GetDeviceInfo(device);
  650. #ifndef WIN32
  651. if(devinfo){
  652. printf("Using device %d: %s\n",device,devinfo->name);
  653. }
  654. #endif
  655. #ifdef WIN32
  656. if(devinfo) {
  657. int apitype = devinfo->hostApi;
  658. const PaHostApiInfo *apiinfo = Pa_GetHostApiInfo(apitype);
  659. printf("Using device %d: %s:\n",device,devinfo->name);
  660. if(apiinfo->type == paDirectSound ){
  661. printf("(DS)\n");
  662. /* set this IF we are using Dsound device. */
  663. directSoundStreamInfo.size = sizeof(PaWinDirectSoundStreamInfo);
  664. directSoundStreamInfo.hostApiType = paDirectSound;
  665. directSoundStreamInfo.version = 2;
  666. directSoundStreamInfo.flags = paWinDirectSoundUseChannelMask;
  667. directSoundStreamInfo.channelMask = 0;
  668. inputParameters.hostApiSpecificStreamInfo = &directSoundStreamInfo;
  669. }
  670. else if(apiinfo->type == paASIO)
  671. printf("(ASIO)\n");
  672. // else
  673. // printf("API unknown!);
  674. }
  675. #endif
  676. // TODO; move this up to before file is created?
  677. err = Pa_IsFormatSupported(&inputParameters, NULL,props.srate);
  678. if(err != paNoError){
  679. printf("Selected device does not support this format.\n");
  680. goto error;
  681. }
  682. err = Pa_OpenStream(
  683. &stream,
  684. &inputParameters,
  685. NULL, /* No output */
  686. props.srate,
  687. frames_per_buffer,
  688. paClipOff,
  689. recordCallback,
  690. &sfdata );
  691. if( err != paNoError ) {
  692. printf("Unable to open output device for %d-channel file.\n",props.chans);
  693. goto error;
  694. }
  695. err = Pa_SetStreamFinishedCallback( stream, finishedCallback );
  696. if( err != paNoError ) {
  697. printf("Internal error: unable to set finish callback\n");
  698. goto error;
  699. }
  700. sfdata.stream = stream;
  701. file_recording = 1;
  702. if(waitkey){
  703. printf("Press any key to start:\n");
  704. while (!kbhit()){
  705. if(!file_recording) //check for instant CTRL-C
  706. goto error;
  707. };
  708. #ifdef WIN32
  709. if(kbhit())
  710. _getch(); //prevent display of char
  711. #endif
  712. }
  713. // set up timer
  714. #ifdef unix
  715. setitimer(ITIMER_REAL, &tout_val,0);
  716. signal(SIGALRM,alarm_wakeup); /* set the Alarm signal capture */
  717. #endif
  718. /* Start the file reading thread */
  719. sfdata.startTime = Pa_GetStreamTime(stream );
  720. err = startThread(&sfdata, threadFunctionWriteFile);
  721. if( err != paNoError ) goto error;
  722. #ifdef WIN32
  723. if(!CreateTimerQueueTimer(&sfdata.hTimer, hTimerQueue,
  724. (WAITORTIMERCALLBACK) TimerCallback, &sfdata,200,200,0)) {
  725. printf("failed to start timer (3).\n");
  726. return 1;
  727. }
  728. #endif
  729. err = Pa_StartStream( stream );
  730. if( err != paNoError )
  731. goto error;
  732. printf("Hit CTRL-C to stop.\n");
  733. while((!sfdata.finished) && file_recording){
  734. // nothing to do!
  735. Pa_Sleep(10);
  736. }
  737. // note to programmer: any bug in audio buffer arithmetic will likely cause crash here!
  738. err = Pa_StopStream( stream );
  739. if( err != paNoError ) {
  740. printf("Error stopping stream\n");
  741. goto error;
  742. }
  743. // need to stop thread explicitly?
  744. err = Pa_CloseStream( stream );
  745. if( err != paNoError ) {
  746. printf("Error closing stream\n");
  747. goto error;
  748. }
  749. #ifdef WIN32
  750. CloseHandle(ghEvent);
  751. DeleteTimerQueue(hTimerQueue);
  752. #endif
  753. printf("%.2f secs\n",(float)(sfdata.lastTime));
  754. fflush(stdout);
  755. printf("Recording finished.\n");
  756. res = psf_sndReadPeaks(sfdata.ofd,fpeaks,(MYLONG *) &lpeaktime);
  757. if(res==0) {
  758. printf("no PEAK data in this soundfile\n");
  759. }
  760. else if(res < 0){
  761. printf("Error reading PEAK data\n");
  762. goto error;
  763. }
  764. else{
  765. // creation time not available until file closed; don't need it here!
  766. printf("PEAK data:\n");
  767. for(i=0;i < sfdata.chans;i++){
  768. if(fpeaks[i].val > 0.0){
  769. double dBval = 20.0 * log10(fpeaks[i].val);
  770. printf("CH %d: %.4f (%.2lfdB) at frame %u: \t%.4f secs\n",
  771. i,fpeaks[i].val,dBval,fpeaks[i].pos,(double)(fpeaks[i].pos / (double) props.srate));
  772. }
  773. else {
  774. printf("CH %d: %.4f (-infdB)at frame %u: \t%.4f secs\n",
  775. i,fpeaks[i].val,fpeaks[i].pos,(double)(fpeaks[i].pos / (double) props.srate));
  776. }
  777. }
  778. }
  779. error:
  780. Pa_Terminate();
  781. //#ifdef _WIN32
  782. // CloseHandle(ghEvent);
  783. // DeleteTimerQueue(hTimerQueue);
  784. //#endif
  785. if( sfdata.ringbufData )
  786. PaUtil_FreeMemory(sfdata.ringbufData);
  787. if(sfdata.ofd >=0)
  788. psf_sndClose(sfdata.ofd);
  789. if(fpeaks)
  790. free(fpeaks);
  791. psf_finish();
  792. return 0;
  793. }
  794. int show_devices(void)
  795. {
  796. // int i,j;
  797. PaDeviceIndex numDevices,p;
  798. const PaDeviceInfo *pdi;
  799. #ifdef _WIN32
  800. const PaHostApiInfo* api_info;
  801. const char *apiname;
  802. #endif
  803. PaError err;
  804. int nInputDevices = 0;
  805. #ifdef USE_ASIO
  806. printf("For ASIO multi-channel, you may need to select the highest device no.\n");
  807. #endif
  808. /*Pa_Initialize();*/
  809. numDevices = Pa_GetDeviceCount();
  810. if( numDevices < 0 )
  811. {
  812. printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
  813. err = numDevices;
  814. return err;
  815. }
  816. #ifdef WIN32
  817. printf("Driver\tDevice\tInput\tOutput\tName\n");
  818. #else
  819. printf("Device\tInput\tOutput\tName\n");
  820. #endif
  821. //printf("Number of devices = %d\n", numDevices );
  822. for( p=0; p<numDevices; p++ )
  823. {
  824. pdi = Pa_GetDeviceInfo( p );
  825. //#ifdef _WIN32
  826. // /*RWD: skip, info on inputs */
  827. // if(pdi->maxOutputChannels == 0)
  828. // continue;
  829. //#endif
  830. nInputDevices++;
  831. if( p == Pa_GetDefaultInputDevice() )
  832. printf("*");
  833. else
  834. printf(" ");
  835. #ifdef WIN32
  836. api_info = Pa_GetHostApiInfo(pdi->hostApi);
  837. apiname = api_info->name;
  838. if(strcmp(apiname,"Windows DirectSound")==0)
  839. apiname = "DS ";
  840. printf("(%s)\t%d\t%d\t%d\t%s\n",apiname,p,
  841. pdi->maxInputChannels,
  842. pdi->maxOutputChannels,
  843. pdi->name);
  844. #else
  845. printf("%d\t%d\t%d\t%s\n",p,
  846. pdi->maxInputChannels,
  847. pdi->maxOutputChannels,
  848. pdi->name);
  849. #endif
  850. }
  851. return 0;
  852. }