rmsinfo.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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. // rmsinfo
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <portsf.h>
  25. #include <math.h>
  26. #include <signal.h>
  27. #ifdef WIN32
  28. # if _MSC_VER && _MSC_VER <= 1200
  29. # include <new.h>
  30. # endif
  31. #endif
  32. #ifdef unix
  33. /* in portsf.lib */
  34. extern "C" {
  35. int stricmp(const char *a, const char *b);
  36. }
  37. #endif
  38. enum {ARG_NAME, ARG_INFILE,ARG_NARGS};
  39. enum {CH_RMS_POWER,CH_AMPSUM,CH_BISUM,CH_NORM_RMS,CH_NORM_AMPSUM};
  40. typedef struct {
  41. double rmspower;
  42. double abssum;
  43. double bisum;
  44. double norm_rms;
  45. double norm_abssum;
  46. } CH_RMSINFO;
  47. #define BUFLEN (1024)
  48. static int scanning(1);
  49. void runhandler(int sig)
  50. {
  51. if(sig == SIGINT){
  52. scanning = 0;
  53. }
  54. }
  55. void usage(){
  56. printf("\nCDP MCTOOLS: RMSINFO v1.0.1 (c) RWD, CDP 2009\n");
  57. printf("Scans infile and reports rms and average loudness (power) of infile,\n"
  58. " relative to digital peak (0dBFS)\n"
  59. );
  60. printf("Usage: rmsinfo [-n] infile [startpos [endpos]]\n"
  61. "Standard output shows:\n"
  62. " RMS level\n"
  63. " Average level\n"
  64. " DC level (bipolar average)\n"
  65. "-n : Include equivalent 0dBFS-normalised RMS and AVG levels.\n"
  66. );
  67. printf("Optional arguments:\n"
  68. "startpos : start file scan from <startpos> seconds.\n"
  69. "endpos : finish file scan at <endpos> seconds.\n"
  70. );
  71. printf("To stop a scan early, use CTRL-C.\n"
  72. "Program will report levels up to that point.\n\n"
  73. );
  74. }
  75. #ifdef WIN32
  76. #if _MSC_VER && _MSC_VER <= 1200
  77. int newhandler(size_t size);
  78. class bad_alloc{};
  79. int newhandler(size_t size)
  80. {
  81. throw bad_alloc();
  82. }
  83. #endif
  84. #endif
  85. int main(int argc, char**argv) {
  86. long inframes,framesread=0,total_framesread;
  87. double maxsamp = 0.0,startpos = 0.0,endpos;
  88. long startframe = 0,endframe;
  89. long halfsecframes = 0;
  90. int i,j,ifd = -1;
  91. int chans;
  92. int do_norm = 0;
  93. PSF_PROPS inprops;
  94. CH_RMSINFO* rmsinfo = NULL;
  95. double* rmsfac = 0;
  96. double* ampsum = 0;
  97. double* ampsumb = 0;
  98. double* inbuf = 0;
  99. double* nrmsfac = 0;
  100. double* nampsum = 0;
  101. /* CDP version number */
  102. if(argc==2 && (stricmp(argv[1],"--version")==0)){
  103. printf("1.0.1\n");
  104. return 0;
  105. }
  106. #ifdef WIN32
  107. # if _MSC_VER && _MSC_VER <= 1200
  108. _set_new_handler( newhandler );
  109. # endif
  110. #endif
  111. if(psf_init()){
  112. puts("unable to start portsf\n");
  113. return 1;
  114. }
  115. if(argc < 2){
  116. usage();
  117. return(1);
  118. }
  119. while(argv[1][0]=='-'){
  120. switch(argv[1][1]){
  121. case 'n':
  122. do_norm = 1;
  123. break;
  124. default:
  125. fprintf(stderr, "Unrecognised flag option %s\n",argv[1]);
  126. return 1;
  127. }
  128. argc--; argv++;
  129. }
  130. if(argc < 2){
  131. usage();
  132. return(1);
  133. }
  134. if((ifd = psf_sndOpen(argv[ARG_INFILE],&inprops, 0)) < 0){
  135. fprintf(stderr,"\nUnable to open input soundfile %s",argv[ARG_INFILE]);
  136. return(1);
  137. }
  138. inframes = psf_sndSize(ifd); // m/c frames
  139. endframe = inframes;
  140. if(inframes <= 0)
  141. return 0;
  142. if(argc >= 3) {
  143. long lpos;
  144. startpos = atof(argv[ARG_INFILE+1]);
  145. if(startpos < 0.0){
  146. fprintf(stderr,"Error: startpos must be positive\n");
  147. return 1;
  148. }
  149. lpos = (long)( startpos * inprops.srate);
  150. if(lpos > inframes){
  151. fprintf(stderr,"Error: startpos value beyond end of file.\n");
  152. return 1;
  153. }
  154. startframe = lpos;
  155. }
  156. if(argc >= 4) {
  157. long lpos;
  158. endpos = atof(argv[ARG_INFILE+2]);
  159. lpos = (long)(endpos * inprops.srate);
  160. if(lpos > inframes){
  161. fprintf(stderr,"Warning: endpos value too large - reset to end of file.\n");
  162. return 1;
  163. }
  164. endframe = lpos;
  165. if(!(endframe > startframe)){
  166. fprintf(stderr,"Error: endpos must be beyond startpos.\n");
  167. return 1;
  168. }
  169. }
  170. if(startframe)
  171. printf("Starting at frame %ld, ending at frame %ld\n",startframe, endframe);
  172. chans = inprops.chans;
  173. try {
  174. inbuf = new double[BUFLEN * chans];
  175. rmsinfo = new CH_RMSINFO[chans];
  176. rmsfac = new double[chans];
  177. ampsum = new double[chans];
  178. ampsumb = new double[chans];
  179. nrmsfac = new double[chans];
  180. nampsum = new double[chans];
  181. }
  182. catch(...){
  183. fputs("no memory!\n",stderr);
  184. return 1;
  185. }
  186. for(i=0; i < chans; i++){
  187. rmsfac[i] = 0.0;
  188. ampsum[i] = 0.0;
  189. ampsumb[i] = 0.0;
  190. nrmsfac[i] = 0.0;
  191. nampsum[i] = 0.0;
  192. }
  193. halfsecframes = inprops.srate / 2;
  194. signal(SIGINT,runhandler);
  195. long wanted = endframe - startframe;
  196. printf("Scanning %ld frames (%.3lf secs):\n",wanted, (double)wanted / inprops.srate);
  197. total_framesread = 0;
  198. if(startframe) {
  199. if(psf_sndSeek(ifd,startframe,PSF_SEEK_SET)){
  200. fprintf(stderr,"File Seek error.\n");
  201. return 1;
  202. }
  203. }
  204. while((framesread = psf_sndReadDoubleFrames(ifd,inbuf,BUFLEN)) > 0){
  205. double fval;
  206. for(i = 0;i < framesread;i++) {
  207. for(j = 0; j < chans; j++){
  208. double val = inbuf[i*chans + j];
  209. fval = fabs(val);
  210. maxsamp = fval > maxsamp ? fval : maxsamp;
  211. ampsum[j] += fval;
  212. rmsfac[j] += val*val;
  213. ampsumb[j] += val;
  214. }
  215. total_framesread++;
  216. if(scanning==0)
  217. break;
  218. if(total_framesread == wanted)
  219. break;
  220. if((total_framesread % halfsecframes) == 0){
  221. printf("%.2lf\r",total_framesread / (double) inprops.srate);
  222. fflush(stdout);
  223. }
  224. }
  225. if(total_framesread == wanted) {
  226. break;
  227. }
  228. }
  229. if(framesread < 0){
  230. fprintf(stderr,"Error reading file.\n");
  231. return 1;
  232. }
  233. for(i=0;i < chans;i++){
  234. rmsfac[i] /= total_framesread;
  235. rmsfac[i] = sqrt(rmsfac[i]);
  236. ampsum[i] /= total_framesread;
  237. ampsumb[i] /= total_framesread;
  238. }
  239. double normfac = 1.0 / maxsamp;
  240. if(scanning==0)
  241. printf("\nScan stopped.\n");
  242. if(total_framesread < inframes){
  243. printf("Scanned %ld frames (%.2lf secs).\n",total_framesread,total_framesread / (double)inprops.srate);
  244. }
  245. printf("Maximum sample = %lf (%.2lfdB)\n",maxsamp,20.0 * log10(maxsamp));
  246. printf("Maximum normalisation factor = %.4f\n",normfac);
  247. for(i=0;i < chans;i++){
  248. rmsinfo[i].rmspower = rmsfac[i];
  249. rmsinfo[i].abssum = ampsum[i];
  250. rmsinfo[i].bisum = ampsumb[i];
  251. rmsinfo[i].norm_rms = normfac * rmsfac[i];
  252. rmsinfo[i].norm_abssum = normfac * ampsum[i];
  253. }
  254. if(do_norm){
  255. printf("\t RMS LEVEL\t AVG \t NET DC\t NORM RMS\t NORM AVG\n");
  256. printf("CH\t AMP\t DB\t AMP\t DB\t AMP\t DB\t AMP\t DB\t AMP\t DB \n");
  257. }
  258. else{
  259. printf("\t RMS LEVEL\t AVG \t NET DC\n");
  260. printf("CH\t AMP\t DB\t AMP\t DB\t AMP\t DB\n");
  261. }
  262. for(i=0;i < chans;i++){
  263. double d1,d2,d3,d4,d5;
  264. d1 = 20*log10(rmsfac[i]);
  265. d2 = 20*log10(ampsum[i]);
  266. d3 = 20*log10(fabs(ampsumb[i]));
  267. d4 = 20*log10(normfac * rmsfac[i]);
  268. d5 = 20*log10(normfac * ampsum[i]);
  269. if(do_norm){
  270. printf("%d\t%.5lf\t%.2lf\t%.5lf\t%.2lf\t%+.4lf\t%.2lf\t%.5lf\t%.2lf\t%.5lf\t%.2lf\n",i+1,
  271. rmsinfo[i].rmspower,d1,
  272. rmsinfo[i].abssum,d2,
  273. rmsinfo[i].bisum,d3,
  274. rmsinfo[i].norm_rms,d4,
  275. rmsinfo[i].norm_abssum,d5
  276. );
  277. }
  278. else {
  279. printf("%d\t%.5lf\t%.2lf\t%.5lf\t%.2lf\t%+.4lf\t%.2lf\n",i+1,
  280. rmsinfo[i].rmspower,d1,
  281. rmsinfo[i].abssum,d2,
  282. rmsinfo[i].bisum,d3
  283. );
  284. }
  285. }
  286. delete [] inbuf;
  287. delete [] rmsfac;
  288. delete [] ampsum;
  289. delete [] ampsumb;
  290. delete [] rmsinfo;
  291. psf_sndClose(ifd);
  292. psf_finish();
  293. return 0;
  294. }