chxformat.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. /*
  2. * Copyright (c) 1983-2023 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. /* chxformat.c */
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <memory.h>
  25. #include <limits.h>
  26. #include <portsf.h>
  27. #ifdef unix
  28. /* in portsf.lib */
  29. extern int stricmp(const char *a, const char *b);
  30. #endif
  31. enum {ARG_PROGNAME, ARG_INFILE,ARG_NARGS};
  32. enum {GUID_PCM,GUID_IEEE,GUID_AMB_PCM,GUID_AMB_IEEE};
  33. char* guidnames[] = {"PCM","PCM FLOAT","AMB PCM","AMB FLOAT"};
  34. #define REVDWBYTES(t) ( (((t)&0xff) << 24) | (((t)&0xff00) << 8) | (((t)&0xff0000) >> 8) | (((t)>>24) & 0xff) )
  35. #define REVWBYTES(t) ( (((t)&0xff) << 8) | (((t)>>8) &0xff) )
  36. #define TAG(a,b,c,d) ( ((a)<<24) | ((b)<<16) | ((c)<<8) | (d) )
  37. #ifdef linux
  38. #define POS64(x) (x.__pos)
  39. #else
  40. #define POS64(x) (x)
  41. #endif
  42. #define WAVE_FORMAT_PCM (0x0001)
  43. #define sizeof_WFMTEX (40)
  44. typedef struct _GUID
  45. {
  46. unsigned int Data1;
  47. unsigned short Data2;
  48. unsigned short Data3;
  49. unsigned char Data4[8];
  50. } GUID;
  51. typedef struct {
  52. WORD wFormatTag;
  53. WORD nChannels;
  54. DWORD nSamplesPerSec;
  55. DWORD nAvgBytesPerSec;
  56. WORD nBlockAlign;
  57. WORD wBitsPerSample;
  58. } WAVEFORMAT;
  59. typedef struct {
  60. WORD wFormatTag;
  61. WORD nChannels;
  62. DWORD nSamplesPerSec;
  63. DWORD nAvgBytesPerSec;
  64. WORD nBlockAlign;
  65. WORD wBitsPerSample;
  66. WORD cbSize;
  67. } WAVEFORMATEX;
  68. typedef struct {
  69. WAVEFORMATEX Format; /* 18 bytes */
  70. union {
  71. WORD wValidBitsPerSample; /* bits of precision */
  72. WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
  73. WORD wReserved; /* If neither applies, set to */
  74. /* zero. */
  75. } Samples;
  76. DWORD dwChannelMask; /* which channels are */
  77. /* present in stream */
  78. GUID SubFormat;
  79. } WAVEFORMATEXTENSIBLE;
  80. static const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x00000001,0x0000,0x0010,
  81. {0x80,
  82. 0x00,
  83. 0x00,
  84. 0xaa,
  85. 0x00,
  86. 0x38,
  87. 0x9b,
  88. 0x71}};
  89. static const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003,0x0000,0x0010,
  90. {0x80,
  91. 0x00,
  92. 0x00,
  93. 0xaa,
  94. 0x00,
  95. 0x38,
  96. 0x9b,
  97. 0x71}};
  98. static const GUID SUBTYPE_AMBISONIC_B_FORMAT_PCM = { 0x00000001, 0x0721, 0x11d3,
  99. { 0x86,
  100. 0x44,
  101. 0xc8,
  102. 0xc1,
  103. 0xca,
  104. 0x0,
  105. 0x0,
  106. 0x0 } };
  107. static const GUID SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT = { 0x00000003, 0x0721, 0x11d3,
  108. { 0x86,
  109. 0x44,
  110. 0xc8,
  111. 0xc1,
  112. 0xca,
  113. 0x0,
  114. 0x0,
  115. 0x0 } };
  116. #define WAVE_FORMAT_IEEE_FLOAT (0x0003)
  117. #define WAVE_FORMAT_EXTENSIBLE (0xfffe)
  118. static int compare_guids(const GUID *gleft, const GUID *gright)
  119. {
  120. const char *left = (const char *) gleft, *right = (const char *) gright;
  121. return !memcmp(left,right,sizeof(GUID));
  122. }
  123. static int wavDoRead(FILE* fp, void* buf, DWORD nBytes)
  124. {
  125. DWORD got = 0;
  126. if((got = fread(buf,sizeof(char),nBytes,fp)) != nBytes) {
  127. return 1;
  128. }
  129. return 0;
  130. }
  131. static int byte_order()
  132. {
  133. int one = 1;
  134. char* endptr = (char *) &one;
  135. return (*endptr);
  136. }
  137. static void fmtSwapBytes(WAVEFORMATEX *pfmt)
  138. {
  139. pfmt->wFormatTag = (WORD) REVWBYTES(pfmt->wFormatTag);
  140. pfmt->nChannels = (WORD) REVWBYTES(pfmt->nChannels);
  141. pfmt->nSamplesPerSec = REVDWBYTES(pfmt->nSamplesPerSec);
  142. pfmt->nAvgBytesPerSec = REVDWBYTES(pfmt->nAvgBytesPerSec);
  143. pfmt->nBlockAlign = (WORD) REVWBYTES(pfmt->nBlockAlign);
  144. pfmt->wBitsPerSample = (WORD) REVWBYTES(pfmt->wBitsPerSample);
  145. }
  146. void printmaskinfo()
  147. {
  148. printf("WAVEX Speaker positions:\n");
  149. printf(
  150. "FRONT LEFT 0x1\n"
  151. "FRONT RIGHT 0x2\n"
  152. "FRONT CENTER 0x4\n"
  153. "LOW FREQUENCY 0x8\n"
  154. "BACK LEFT 0x10\n"
  155. "BACK RIGHT 0x20\n"
  156. "FRONT LEFT OF CENTER 0x40\n"
  157. "FRONT RIGHT OF CENTER 0x80\n"
  158. "BACK CENTER 0x100\n"
  159. "SIDE LEFT 0x200\n"
  160. "SIDE RIGHT 0x400\n"
  161. "TOP CENTER 0x800\n"
  162. "TOP FRONT LEFT 0x1000\n"
  163. "TOP FRONT CENTER 0x2000\n"
  164. "TOP FRONT RIGHT 0x4000\n"
  165. "TOP BACK LEFT 0x8000\n"
  166. "TOP BACK CENTER 0x10000\n"
  167. "TOP BACK RIGHT 0x20000\n"
  168. );
  169. printf("The value 0x80000000 is reserved.\n");
  170. printf("Standard layouts:\n"
  171. "Mono 0x40 (64)\n"
  172. "Stereo 0x03 (3)\n"
  173. "Quad 0x33 (51)\n"
  174. "LCRS 0x107 (263)\n"
  175. "5.0 0x37 (55)\n"
  176. "5.1 0x3f (63)\n"
  177. "7.1 0xff (255)\n"
  178. "Cube 0x2d033 (184371)\n"
  179. );
  180. }
  181. void usage(void)
  182. {
  183. printf("CDP MCTOOLS: CHXFORMAT v1.0.1beta (c) RWD,CDP 2009\n");
  184. printf("change GUID and/or speaker mask in WAVEX header of infile.\n");
  185. printf("usage: chxformat [-m | [[-t] [-gGUID] [-sMASK]] filename\n");
  186. printf(
  187. " -gGUID : change GUID type between PCM and AMB.\n"
  188. " Plain WAVEX: GUID = 1\n"
  189. " AMB: GUID = 2\n"
  190. " -sMASK : change speaker position mask.\n"
  191. " MASK = 0: unset channel mask\n"
  192. " else set to MASK\n"
  193. " MASK may be given in decimal or hex (prefix '0x').\n"
  194. " -m : (not in combination with other options)\n"
  195. " NOTE: speaker positions are only supported for WAVEX PCM files.\n"
  196. " If GUID is set to 2, the -s option should not be used. Any existing\n"
  197. " speaker positions will be set to zero.\n"
  198. " Type chxformat -m to see list of WAVEX mask values.\n"
  199. " -t : Test only: do not modify file.\n"
  200. " If only infile given, program prints current GUID type and speaker mask.\n"
  201. " In test mode, program checks file, prints current channel mask as binary,\n"
  202. " and new mask in binary, if -s option set.\n"
  203. " Otherwise: program modifies infile (\"destructively\") - use with care!\n"
  204. );
  205. }
  206. void binprintf(int val,int skipzeroes)
  207. {
  208. unsigned int nbits = sizeof(int) * CHAR_BIT;
  209. unsigned int i = 0;
  210. unsigned int mask = 1 << (nbits-1); /* i.e. 0x80000000; */
  211. // skip leading zeroes
  212. if(skipzeroes){
  213. for(;i < nbits;i++){
  214. if(val&mask)
  215. break;
  216. mask>>=1;
  217. }
  218. }
  219. for(;i < nbits;i++){
  220. if(i > 0 && i%8 == 0)
  221. printf(" ");
  222. printf("%d",(val & mask) ? 1 : 0);
  223. mask >>= 1;
  224. }
  225. }
  226. int countbits(int val)
  227. {
  228. unsigned int nbits = sizeof(int) * CHAR_BIT;
  229. unsigned int i = 0;
  230. int count = 0;
  231. unsigned int mask = 1 << (nbits-1); /* i.e. 0x80000000; */
  232. for(;i< nbits;i++){
  233. if(val & mask)
  234. count++;
  235. mask >>= 1;
  236. }
  237. return count;
  238. }
  239. int isdec(int ch){
  240. if(ch >= '0' && ch <= '9')
  241. return 1;
  242. // printf("isdec fail: %d\n",ch);
  243. return 0;
  244. }
  245. int ishex(int ch){
  246. if((ch >='A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))
  247. return 1;
  248. // printf("ishex fail: %c\n",ch);
  249. return 0;
  250. }
  251. int validate_mask(const char* mask)
  252. {
  253. int is_dec = 1,is_hex = 1;
  254. size_t i,len;
  255. char* pch;
  256. if(mask==NULL)
  257. return 0;
  258. len = strlen(mask);
  259. if(len==0)
  260. return 0;
  261. pch = (char*) mask;
  262. /*try hex */
  263. if(len > 2 && *pch =='0'){
  264. pch++;
  265. if(*pch=='x' || *pch =='X'){ // prefix found
  266. pch++;
  267. // printf("got hex prefix\n");
  268. for(i = 0;i < len-2;i++){
  269. // printf("testing %d \n",pch[i]);
  270. if(!(isdec(pch[i]) || ishex(pch[i]))){
  271. is_hex = 0;
  272. break;
  273. }
  274. }
  275. if(is_hex)
  276. return 1;
  277. }
  278. // not hex, maybe decimal
  279. }
  280. pch = (char*) mask;
  281. for(i=0;i < len;i++){
  282. if(!isdec(pch[i])){
  283. is_dec = 0;
  284. break;
  285. }
  286. }
  287. return is_dec;
  288. }
  289. int main (int argc, char**argv)
  290. {
  291. int do_changeguid = 0;
  292. int in_guidtype =0;
  293. int guidtoset = 0;
  294. int do_changemask = 0;
  295. unsigned int speakermask = 0;
  296. int fmtfound = 0;
  297. int test = 0;
  298. int src_is_amb = 0;
  299. FILE* fp = NULL;
  300. char* maskstring = NULL;
  301. psf_format outformat=PSF_FMT_UNKNOWN;
  302. psf_format new_outtype=PSF_FMT_UNKNOWN;
  303. DWORD tag;
  304. DWORD size;
  305. fpos_t bytepos;
  306. DWORD fmtoffset = 0;
  307. DWORD guidoffset = 0;
  308. DWORD maskoffset = 0;
  309. WORD cbSize;
  310. WORD validbits;
  311. DWORD chmask;
  312. WAVEFORMATEXTENSIBLE fmt;
  313. int is_little_endian = byte_order();
  314. /* CDP version number */
  315. if(argc==2 && (stricmp(argv[1],"--version")==0)){
  316. printf("1.0.1b\n");
  317. return 0;
  318. }
  319. if(argc<2){
  320. usage();
  321. return 1;
  322. }
  323. while(argv[1][0]=='-'){
  324. switch(argv[1][1]){
  325. case 'm':
  326. if(argc > 2){
  327. printf("cannot use -m with other options\n");
  328. return 1;
  329. }
  330. else{
  331. printmaskinfo();
  332. return 0;
  333. }
  334. break;
  335. case 'g':
  336. if(do_changeguid){
  337. printf("Cannot use -g option more than once!\n");
  338. return 1;
  339. }
  340. guidtoset = atoi(&argv[1][2]);
  341. if(guidtoset < 1 || guidtoset > 2) {
  342. printf("bad value for -g option - use 1 or 2 only.\n");
  343. return 1;
  344. }
  345. do_changeguid = 1;
  346. break;
  347. case 's':
  348. if(do_changemask){
  349. printf("Cannot use -s option more than once!\n");
  350. return 1;
  351. }
  352. maskstring = &argv[1][2];
  353. if(validate_mask(maskstring)==0){
  354. printf("Bad format for mask argument.\n"
  355. "Value must be decimal, or hex with 0x prefix.\n");
  356. return 1;
  357. }
  358. do_changemask = 1;
  359. break;
  360. case 't':
  361. if(test==1){
  362. printf("You only need to set -t once!\n");
  363. }
  364. else
  365. printf("Test mode: no changes will be made.\n");
  366. test = 1;
  367. break;
  368. default:
  369. printf("illegal option %s\n",argv[1]);
  370. return 1;
  371. }
  372. argc--;
  373. argv++;
  374. }
  375. if(argc != 2){
  376. printf("infile argument required.\n");
  377. usage();
  378. return 1;
  379. }
  380. psf_init();
  381. /* nitty-gritty to read header */
  382. outformat = psf_getFormatExt(argv[ARG_INFILE]);
  383. if(!(outformat == PSF_STDWAVE|| outformat==PSF_WAVE_EX)){
  384. printf("file mustbe WAVEX format with .wav or .amb extension.\n");
  385. return 1;
  386. }
  387. fp = fopen(argv[ARG_INFILE],"rb+");
  388. if(fp==NULL){
  389. printf("unable to open infile %s\n",argv[ARG_INFILE]);
  390. return 1;
  391. }
  392. if(wavDoRead(fp,(char *)&tag,sizeof(DWORD))
  393. || wavDoRead(fp,(char *) &size,sizeof(DWORD))) {
  394. printf("read error 1\n");
  395. return 1;
  396. }
  397. if(!is_little_endian)
  398. size = REVDWBYTES(size);
  399. else
  400. tag = REVDWBYTES(tag);
  401. if(tag != TAG('R','I','F','F')){
  402. printf("not a RIFF file\n");
  403. return 1;
  404. }
  405. if(size < (sizeof(WAVEFORMAT) + 3 * sizeof(WORD))){
  406. printf("file has bad header.\n");
  407. return 1;
  408. }
  409. if(wavDoRead(fp,(char *)&tag,sizeof(DWORD))) {
  410. printf("read error 2\n");
  411. return 1;
  412. }
  413. if(is_little_endian)
  414. tag = REVDWBYTES(tag);
  415. if(tag != TAG('W','A','V','E')){
  416. printf("Not a WAVE file.\n");
  417. return 1;
  418. }
  419. for(;;){
  420. if(fmtfound)
  421. break;
  422. if(wavDoRead(fp,(char *)&tag,sizeof(DWORD))
  423. || wavDoRead(fp,(char *) &size,sizeof(DWORD))) {
  424. printf("read error 3\n");
  425. return 1;
  426. }
  427. if(!is_little_endian)
  428. size = REVDWBYTES(size);
  429. else
  430. tag = REVDWBYTES(tag);
  431. switch(tag){
  432. case(TAG('f','m','t',' ')):
  433. if( size < sizeof(WAVEFORMAT)){
  434. printf("file has bad format.\n");
  435. return 1;
  436. }
  437. if(size > sizeof_WFMTEX) {
  438. printf("file has unsupported WAVE format.\n");
  439. return 1;
  440. }
  441. if(fgetpos(fp,&bytepos)) {
  442. printf("seek error\n");
  443. return 1;
  444. }
  445. fmtoffset = (DWORD) POS64(bytepos);
  446. if(wavDoRead(fp,(char *) &fmt.Format,sizeof(WAVEFORMAT))){
  447. printf("read error 4\n");
  448. return 1;
  449. }
  450. if(!is_little_endian)
  451. fmtSwapBytes(&fmt.Format);
  452. /* calling function decides if format is supported*/
  453. if(size > sizeof(WAVEFORMAT)) {
  454. if(wavDoRead(fp,(char*)&cbSize,sizeof(WORD))){
  455. printf("read error 5\n");
  456. return 1;
  457. }
  458. if(!is_little_endian)
  459. cbSize = (WORD) REVWBYTES(cbSize);
  460. if(cbSize==0){
  461. printf("file is plain WAVE format.\n");
  462. return 1;
  463. }
  464. if(fmt.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE){
  465. if(cbSize != 22) {
  466. printf("not a recognized WAVEX file.\n");
  467. return 1;
  468. }
  469. }
  470. if(wavDoRead(fp,(char *) &validbits,sizeof(WORD))){
  471. printf("read error 6\n");
  472. return 1;
  473. }
  474. if(!is_little_endian)
  475. validbits = REVWBYTES(validbits);
  476. fmt.Samples.wValidBitsPerSample = (WORD) validbits;
  477. if(wavDoRead(fp,(char *) &chmask,sizeof(DWORD))) {
  478. printf("read error 7\n");
  479. return 1;
  480. }
  481. if(!is_little_endian)
  482. chmask = REVDWBYTES(chmask);
  483. fmt.dwChannelMask = chmask;
  484. if(wavDoRead(fp,(char *) &(fmt.SubFormat),sizeof(GUID))) {
  485. printf("read error 8 \n");
  486. return 1;
  487. }
  488. if(!is_little_endian){
  489. fmt.SubFormat.Data1 = REVDWBYTES(fmt.SubFormat.Data1);
  490. fmt.SubFormat.Data2 = (WORD) REVWBYTES(fmt.SubFormat.Data2);
  491. fmt.SubFormat.Data3 = (WORD) REVWBYTES(fmt.SubFormat.Data3);
  492. }
  493. /* if we get a good GUID, we are ready to make changes! */
  494. if(compare_guids(&(fmt.SubFormat),&(KSDATAFORMAT_SUBTYPE_PCM))) {
  495. in_guidtype = GUID_PCM;
  496. if(test)
  497. printf("Current GUID: KSDATAFORMAT_SUBTYPE_PCM.\n");
  498. }
  499. else if(compare_guids(&(fmt.SubFormat),&(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) {
  500. in_guidtype = GUID_IEEE;
  501. if(test)
  502. printf("Current GUID: KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.\n");
  503. }
  504. else if(compare_guids(&(fmt.SubFormat),&(SUBTYPE_AMBISONIC_B_FORMAT_PCM))) {
  505. in_guidtype = GUID_AMB_PCM;
  506. src_is_amb = 1;
  507. if(test)
  508. printf("Current GUID: SUBTYPE_AMBISONIC_B_FORMAT_PCM.\n");
  509. }
  510. else if(compare_guids(&(fmt.SubFormat),&(SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT))){
  511. in_guidtype = GUID_AMB_IEEE;
  512. src_is_amb = 1;
  513. if(test)
  514. printf("Current GUID: SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT.\n");
  515. }
  516. else {
  517. printf("unrecognized WAVE_EX GUID.\n");
  518. return 1;
  519. }
  520. }
  521. else {
  522. printf("WAVEX format required.\n"
  523. "Use copysfx to convert to WAVEX format.\n");
  524. return 1;
  525. }
  526. fmtfound = 1;
  527. break;
  528. case TAG('d','a','t','a'):
  529. if(!fmtfound){
  530. printf("bad WAVE file: no fmt chunk found!\n");
  531. return 1;
  532. }
  533. default:
  534. /* unknown chunk - skip */
  535. if(!fmtfound) {
  536. if(fseek(fp,size,SEEK_CUR)){
  537. printf("seek error\n");
  538. return 1;
  539. }
  540. }
  541. break;
  542. }
  543. }
  544. if(!fmtfound){
  545. printf("no fmt chunk found!\n");
  546. return 1;
  547. }
  548. maskoffset = fmtoffset + sizeof(WAVEFORMAT) + sizeof(WORD)*2;
  549. guidoffset = maskoffset + sizeof(DWORD);
  550. if(!(do_changeguid || do_changemask)){
  551. /* display what we know */
  552. printf("Wordsize : %d\n",validbits);
  553. printf("Channels : %d\n",fmt.Format.nChannels);
  554. printf("GUID : %s\n",guidnames[in_guidtype]);
  555. if(chmask==0)
  556. printf("Channel mask : 0\n");
  557. else
  558. printf("Channel mask : 0x%06x (%d)\n",(unsigned int) chmask,(int) chmask);
  559. if(chmask){
  560. printf("Channel mask (binary): ");
  561. binprintf(chmask,1);
  562. printf("There are %d speaker positions set.\n",countbits(chmask));
  563. }
  564. printf("\n");
  565. }
  566. else {
  567. int bits,is_same = 0;
  568. /* check all settings before making any changes */
  569. if(do_changemask){
  570. // be strict!
  571. if(src_is_amb && guidtoset != 1){
  572. printf("-s flag only supported for PCM files (AMB speaker mask must be zero).\n"
  573. "Exiting.\n");
  574. return 1;
  575. }
  576. if(strncmp(maskstring,"0x",2)==0)
  577. sscanf(maskstring,"%x", &speakermask);
  578. else
  579. sscanf(maskstring,"%u", &speakermask);
  580. bits = countbits(speakermask);
  581. /* can't do much if a silly huge value given */
  582. if(speakermask < 0 || speakermask >= 0x40000){
  583. printf("Mask value out of range!\n");
  584. return 1;
  585. }
  586. if(bits > fmt.Format.nChannels){
  587. printf("Error: %d positions requested for %d channels: \n",bits,fmt.Format.nChannels);
  588. binprintf(speakermask,1);
  589. printf("\n");
  590. return 1;
  591. }
  592. if(bits && bits < fmt.Format.nChannels){
  593. printf("Warning: %d requested positions less than channels in file.\n",bits);
  594. }
  595. }
  596. if(do_changeguid){
  597. GUID nuguid;
  598. // set float or pcm GUID as required.
  599. // no copy to same: only convert if types are different
  600. switch(guidtoset){
  601. case 1: // plain wavex - the easy iption
  602. if(in_guidtype == GUID_AMB_PCM){
  603. nuguid = KSDATAFORMAT_SUBTYPE_PCM;
  604. }
  605. else if(in_guidtype == GUID_AMB_IEEE) {
  606. nuguid = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  607. }
  608. else
  609. is_same = 1;
  610. new_outtype = PSF_STDWAVE;
  611. break;
  612. case 2: // convert to amb - must zero speaker flags
  613. if(in_guidtype == GUID_PCM)
  614. nuguid = SUBTYPE_AMBISONIC_B_FORMAT_PCM;
  615. else if(in_guidtype == GUID_IEEE)
  616. nuguid = SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT;
  617. else
  618. is_same = 1;
  619. // set mask to zero if setting AMB format
  620. if(is_same==0) {
  621. do_changemask = 1;
  622. maskstring = "0";
  623. speakermask = 0;
  624. }
  625. new_outtype = PSF_WAVE_EX;
  626. break;
  627. }
  628. if(is_same){
  629. printf("new GUID identical - no change made.\n");
  630. }
  631. else {
  632. int err;
  633. //make the change!
  634. if(!is_little_endian){
  635. nuguid.Data1 = REVDWBYTES(nuguid.Data1);
  636. nuguid.Data2 = (WORD) REVWBYTES(nuguid.Data2);
  637. nuguid.Data3 = (WORD) REVWBYTES(nuguid.Data3);
  638. }
  639. if(!test){
  640. if(fseek(fp,guidoffset,SEEK_SET) !=0){
  641. printf("seek error updating channelmask. exiting.\n");
  642. return 1;
  643. }
  644. err = fwrite((char*) &nuguid,1,sizeof(GUID),fp);
  645. if(err != sizeof(GUID)){
  646. printf("Error updating GUID. File may have inconsistent header.\n");
  647. return 1;
  648. }
  649. printf("new GUID set.\n");
  650. }
  651. }
  652. }
  653. if(do_changemask){
  654. /* read user mask value */
  655. /* TODO: full parse, trap bad characters */
  656. if(chmask==speakermask){
  657. if(speakermask > 0)
  658. printf("Requested mask is already set. Data not modified.\n");
  659. }
  660. else {
  661. DWORD writemask;
  662. int err;
  663. if(speakermask > 0){
  664. printf("new mask = %d (",speakermask);
  665. binprintf(speakermask,1);
  666. printf(")\n");
  667. }
  668. if(fseek(fp,maskoffset,SEEK_SET) !=0){
  669. printf("seek error updating channelmask. exiting.\n");
  670. return 1;
  671. }
  672. writemask = speakermask;
  673. if(!is_little_endian)
  674. writemask = REVDWBYTES(speakermask);
  675. if(!test){
  676. err = fwrite((char*) &writemask,1,sizeof(DWORD),fp);
  677. if(err != sizeof(DWORD)){
  678. printf("Error updating mask. File may have inconsistent header.\n");
  679. return 1;
  680. }
  681. printf("New mask set.\n");
  682. }
  683. }
  684. }
  685. if(outformat == PSF_STDWAVE && new_outtype==PSF_WAVE_EX)
  686. printf("File extension should be changed to amb.\n");
  687. else if(outformat == PSF_WAVE_EX && new_outtype == PSF_STDWAVE)
  688. printf("File extension should be changed to wav.\n");
  689. }
  690. fseek(fp,0,SEEK_END);
  691. fclose(fp);
  692. return 0;
  693. }