1 // Copyright (C) 1999-2005 Open Source Telecom Corporation.
2 // Copyright (C) 2006-2014 David Sugar, Tycho Softworks.
3 // Copyright (C) 2015 Cherokees of Idaho.
4 //
5 // This file is part of GNU ccAudio2.
6 //
7 // GNU ccAudio2 is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Lesser General Public License as published
9 // by the Free Software Foundation, either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // GNU ccAuydio2 is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU Lesser General Public License for more details.
16 //
17 // You should have received a copy of the GNU Lesser General Public License
18 // along with GNU ccAudio2.  If not, see <http://www.gnu.org/licenses/>.
19 
20 #include <ccaudio2.h>
21 #include <ccaudio2-config.h>
22 #ifdef  HAVE_ENDIAN_H
23 #include <sys/endian.h>
24 #ifndef __BYTE_ORDER
25 #define __LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN)
26 #define __BIG_ENDIAN    (_BYTE_ORDER == _BIG_ENDIAN)
27 #define __BYTE_ORDER _BYTE_ORDER
28 #endif
29 #endif
30 
31 #if !defined(__BIG_ENDIAN)
32 #define __LITTLE_ENDIAN 1234
33 #define __BIG_ENDIAN    4321
34 #define __PDP_ENDIAN    3412
35 #define __BYTE_ORDER    __LITTLE_ENDIAN
36 #endif
37 
38 using namespace ucommon;
39 
40 static const char *delfile = NULL;
41 
42 class AudioBuild : public AudioStream
43 {
44 private:
45     char **list;
46     char *getContinuation(void);
47 public:
48     AudioBuild();
49     void open(char **argv);
50 
51     static void copyDirect(AudioStream &input, AudioStream &output);
52     static void copyConvert(AudioStream &input, AudioStream &output);
53 };
54 
55 class PacketStream : public AudioStream
56 {
57 private:
58     char **list;
59     char *getContinuation(void);
60 
61 public:
62     PacketStream();
63     void open(char **argv);
64 };
65 
66 class PlayStream : public AudioStream
67 {
68 private:
69     char **list;
70     char *getContinuation(void);
71 
72 public:
73     PlayStream();
74     void open(char **argv);
75 };
76 
PlayStream()77 PlayStream::PlayStream() : AudioStream()
78 {
79     list = NULL;
80 }
81 
open(char ** argv)82 void PlayStream::open(char **argv)
83 {
84     AudioStream::open(*(argv++), modeRead, 10);
85     if(is_open())
86         list = argv;
87 }
88 
getContinuation(void)89 char *PlayStream::getContinuation(void)
90 {
91     if(!list)
92         return NULL;
93 
94     return *(list++);
95 }
96 
PacketStream()97 PacketStream::PacketStream() : AudioStream()
98 {
99     list = NULL;
100 }
101 
open(char ** argv)102 void PacketStream::open(char **argv)
103 {
104     AudioStream::open(*(argv++), modeRead, 10);
105     if(is_open())
106         list = argv;
107 }
108 
getContinuation(void)109 char *PacketStream::getContinuation(void)
110 {
111     if(!list)
112         return NULL;
113 
114     return *(list++);
115 }
116 
117 
AudioBuild()118 AudioBuild::AudioBuild() : AudioStream()
119 {
120     list = NULL;
121 }
122 
open(char ** argv)123 void AudioBuild::open(char **argv)
124 {
125     AudioStream::open(*(argv++), modeRead, 10);
126     if(is_open())
127         list = argv;
128 }
129 
getContinuation(void)130 char *AudioBuild::getContinuation(void)
131 {
132     if(!list)
133         return NULL;
134 
135     return *(list++);
136 }
137 
copyConvert(AudioStream & input,AudioStream & output)138 void AudioBuild::copyConvert(AudioStream &input, AudioStream &output)
139 {
140     size_t samples, copied;
141     Linear buffer, source;
142     size_t pages, npages;
143     Info from, to;
144     bool mono = true;
145     AudioResample *resampler = NULL;
146     Linear resample = NULL;
147 
148     input.getInfo(&from);
149     output.getInfo(&to);
150 
151     if(is_stereo(from.encoding) || is_stereo(to.encoding))
152         mono = false;
153 
154     samples = input.getCount();
155 
156 
157     if(mono)
158         buffer = new Audio::Sample[samples];
159     else
160         buffer = new Audio::Sample[samples * 2];
161 
162     source = buffer;
163 
164     if(from.rate != to.rate) {
165         resampler = new AudioResample((Audio::Rate)from.rate, (Audio::Rate)to.rate);
166         resample = new Audio::Sample[resampler->estimate(samples)];
167         source = resample;
168     }
169 
170     for(;;)
171     {
172         if(mono)
173             pages = input.getMono(buffer, 1);
174         else
175             pages = input.getStereo(buffer, 1);
176 
177         if(!pages)
178             break;
179 
180         if(resampler)
181             copied = resampler->process(buffer, resample, samples);
182         else
183             copied = samples;
184 
185         if(mono)
186             npages = output.bufMono(source, (unsigned)copied);
187         else
188             npages = output.bufStereo(source, (unsigned)copied);
189 
190         if(!npages)
191             break;
192     }
193 
194     delete[] buffer;
195 
196     if(resampler)
197         delete resampler;
198 
199     if(resample)
200         delete[] resample;
201 }
202 
copyDirect(AudioStream & input,AudioStream & output)203 void AudioBuild::copyDirect(AudioStream &input, AudioStream &output)
204 {
205     unsigned bufsize;
206     Encoded buffer;
207     Info from, to;
208 //    bool endian = false;
209     ssize_t status = 1;
210 
211     input.getInfo(&from);
212     output.getInfo(&to);
213 
214 //    if(to.order && from.order && to.order != from.order && is_linear(from.encoding))
215 //        endian = true;
216 
217     bufsize = from.framesize;
218     buffer = new unsigned char[bufsize];
219 
220     while(status > 0) {
221         status = input.getNative(buffer, bufsize);
222         if(status < 1)
223             break;
224 
225         status = output.putNative(buffer, status);
226     }
227 
228     delete[] buffer;
229 }
230 
231 
stop(void)232 static void stop(void)
233 {
234     if(delfile) {
235         remove(delfile);
236         delfile = NULL;
237     }
238 }
239 
plugins(void)240 static void plugins(void)
241 {
242     printf("%s\n", Audio::getPluginPath());
243     exit(0);
244 }
245 
codecs(void)246 static void codecs(void)
247 {
248     linked_pointer<AudioCodec> cp = AudioCodec::begin();
249 
250     while(is(cp)) {
251         printf("%s - %s\n", cp->getName(), cp->getDescription());
252         cp.next();
253     }
254     exit(0);
255 }
256 
version(void)257 static void version(void)
258 {
259     printf("%s\n", VERSION);
260     exit(0);
261 }
262 
soundcard(unsigned index)263 static void soundcard(unsigned index)
264 {
265     AudioDevice *soundcard = Audio::getDevice(index);
266     const Audio::Info *info;
267 
268     if(!Audio::is_available(index) && !soundcard)
269         printf("%s\n", _TEXT("Sound device inaccessible or unsupported"));
270     else if(!soundcard)
271         printf("%s\b", _TEXT("Sound device unavailable"));
272     else {
273         info = soundcard->getInfo();
274         printf("%s: %s\n", _TEXT("Soundcard Driver"), info->annotation);
275         if(Audio::is_stereo(info->encoding))
276             printf("%s: 2\n", _TEXT("Default Channels"));
277         else
278             printf("%s: 1\n", _TEXT("Default Channels"));
279         printf("%s: %s\n", _TEXT("Default Encoding"), Audio::getName(info->encoding));
280         printf("%s: %d\n", _TEXT("Default Buffers"), info->framecount);
281         printf("%s: %ldms\n", _TEXT("Default Framing"), info->framing);
282         printf("%s: %ld %s\n", _TEXT("Default Rate"), info->rate, _TEXT(" samples per second"));
283     }
284     exit(0);
285 }
286 
showendian(void)287 static void showendian(void)
288 {
289     if(__BYTE_ORDER == __BIG_ENDIAN)
290         printf("%s\n", _TEXT("big"));
291     else
292         printf("%s\n", _TEXT("little"));
293     exit(0);
294 }
295 
fname(const char * cp)296 static const char *fname(const char *cp)
297 {
298     const char *fn = strrchr(cp, '/');
299     if(!fn)
300         fn = strrchr(cp, '\\');
301     if(fn)
302         return ++fn;
303     return cp;
304 }
305 
rewrite(const char * source,char * target,size_t max)306 static void rewrite(const char *source, char *target, size_t max)
307 {
308     char *fn;
309     char buffer[256];
310 
311 #ifdef  WIN32
312     char *ext;
313     snprintf(buffer, sizeof(buffer), "%s", source);
314     while(NULL != (fn = strchr(buffer, '\\')))
315         *fn = '/';
316 
317     fn = strrchr(buffer, '/');
318     if(fn) {
319         *(fn++) = 0;
320         ext = strrchr(fn, '.');
321         if(ext)
322             *ext = 0;
323         snprintf(target, max, "%s/%s.tmp", buffer, fn);
324     }
325     else {
326         ext = strrchr(buffer, '.');
327         if(ext)
328             *ext = 0;
329         snprintf(target, max, "%s.tmp", buffer);
330     }
331 #else
332     snprintf(buffer, sizeof(buffer), "%s", source);
333     fn = strrchr(buffer, '/');
334     if(fn) {
335         *(fn++) = 0;
336         snprintf(target, max, "%s/.tmp.%s", buffer, fn);
337     }
338     else
339         snprintf(target, max, ".tmp.%s", source);
340 #endif
341 }
342 
chart(char ** argv)343 static void chart(char **argv)
344 {
345     AudioFile file;
346     Audio::Info info;
347     AudioCodec *codec = NULL;
348     char *fn;
349     timeout_t framing = 20;
350     Audio::Level silence = 0;
351     unsigned char *buffer;
352     short max, current;
353     unsigned long sum;
354     unsigned long count;
355 
356 retry:
357     if(!*argv) {
358         shell::errexit(2, "*** audiotool: -chart: %s\n",
359             _TEXT("missing arguments"));
360     }
361 
362     fn = *argv;
363 
364     if(eq(fn, "--")) {
365         ++argv;
366         goto skip;
367     }
368 
369     if(eq(fn, "--", 2))
370         ++fn;
371 
372     if(eq(fn, "-framing=", 9)) {
373         framing = atoi(fn + 9);
374         ++argv;
375         goto retry;
376     }
377     else if(eq(fn, "-framing"))
378     {
379         ++argv;
380         if(!*argv) {
381             shell::errexit(3, "*** audiotool: -chart: -framing: %s\n",
382                 _TEXT("missing argument"));
383         }
384         framing = atoi(*(argv++));
385         goto retry;
386     }
387 
388     if(eq(fn, "-silence=", 9)) {
389         silence = atoi(fn + 9);
390         ++argv;
391         goto retry;
392     }
393     else if(eq(fn, "-silence"))
394     {
395         ++argv;
396         if(!*argv) {
397             shell::errexit(3, "*** audiotool: -chart: -silence: %s\n",
398                 _TEXT("missing argument"));
399         }
400         silence = atoi(*(argv++));
401         goto retry;
402     }
403 
404 skip:
405 
406     if(!framing)
407         framing = 20;
408 
409     while(*argv) {
410         if(!fsys::is_file(*argv)) {
411             printf("%s: %s\n",
412                 fname(*(argv++)), _TEXT("invalid"));
413             continue;
414         }
415         if(!fsys::is_readable(*argv)) {
416             printf("%s: %s\n",
417                 fname(*(argv++)), _TEXT("inaccessable"));
418             continue;
419         }
420         file.open(*argv, Audio::modeRead, framing);
421         file.getInfo(&info);
422         if(!Audio::is_linear(info.encoding))
423             codec = AudioCodec::get(info);
424         if(!Audio::is_linear(info.encoding) && !codec) {
425             printf("%s: %s\n",
426                 fname(*(argv++)), _TEXT("cannot load codec"));
427             continue;
428         }
429 
430         printf("%s: ", fname(*(argv++)));
431         buffer = new unsigned char[info.framesize];
432 
433         max = 0;
434         sum = 0;
435         count = 0;
436 
437         // autochart for silence value
438 
439         while(!silence) {
440             if(file.getBuffer(buffer, info.framesize) < (int)info.framesize)
441                 break;
442             ++count;
443             if(codec)
444                 sum += codec->impulse(buffer, info.framecount);
445             else
446                 sum += Audio::impulse(info, buffer, info.framecount);
447         }
448 
449         if(!silence && count)
450             silence = (Audio::Level)(((sum / count) * 2) / 3);
451 
452         max = 0;
453         sum = 0;
454         count = 0;
455 
456         file.setPosition(0);
457 
458         for(;;) {
459             if(file.getBuffer(buffer, info.framesize) < (int)info.framesize)
460                 break;
461             ++count;
462             if(codec) {
463                 current = codec->peak(buffer, info.framecount);
464                 if(current > max)
465                     max = current;
466                 sum += codec->impulse(buffer, info.framecount);
467                 if(codec->is_silent(silence, buffer, info.framecount)) {
468                     if(codec->peak(buffer, info.framecount) >= silence)
469                         printf("^");
470                     else
471                         printf(".");
472                 }
473                 else
474                     printf("+");
475                 fflush(stdout);
476                 continue;
477             }
478 
479             current = Audio::peak(info, buffer, info.framecount);
480             if(current > max)
481                 max = current;
482 
483             sum += Audio::impulse(info, buffer, info.framecount);
484             if(Audio::impulse(info, buffer, info.framecount) < silence) {
485                 if(Audio::peak(info, buffer, info.framecount) >= silence)
486                     printf("^");
487                 else
488                     printf(".");
489             }
490             else
491                 printf("+");
492             fflush(stdout);
493         }
494         printf("\n");
495         if(count)
496             printf("%s = %d, %s = %ld, %s = %d\n",
497                 _TEXT("silence threashold"), silence,
498                 _TEXT("avg frame energy"), (sum / count),
499                 _TEXT("peak level"), max);
500 
501         if(buffer)
502             delete[] buffer;
503 
504 
505         if(codec)
506             AudioCodec::release(codec);
507 
508         codec = NULL;
509         buffer = NULL;
510 
511         file.close();
512     }
513     exit(0);
514 }
515 
info(char ** argv)516 static void info(char **argv)
517 {
518     AudioFile au;
519     Audio::Info info;
520     const char *fn;
521     timeout_t framing = 0;
522     unsigned long end;
523     unsigned long minutes, seconds, subsec, scale;
524 
525     fn = *argv;
526     if(eq(fn, "--", 2))
527         ++fn;
528     if(eq(fn, "-framing=", 9)) {
529         framing = atoi(fn + 9);
530         ++argv;
531     }
532     else if(eq(fn, "-framing"))
533     {
534         framing = atoi(*(++argv));
535         ++argv;
536     }
537 
538     while(*argv) {
539         if(!fsys::is_file(*argv)) {
540             printf("audiotool: %s: %s\n",
541                 fname(*(argv++)), _TEXT("invalid"));
542             continue;
543         }
544         if(fsys::is_readable(*argv)) {
545             printf("audiotool: %s: %s\n",
546                 fname(*(argv++)), _TEXT("inaccessable"));
547             continue;
548         }
549         au.open(*argv, Audio::modeInfo, framing);
550         au.getInfo(&info);
551         au.setPosition();
552         end = au.getPosition();
553         printf("%s\n", fname(*(argv++)));
554         fn = Audio::getMIME(info);
555         if(!fn)
556             switch(info.format) {
557             case Audio::raw:
558                 fn = "raw audio";
559                 break;
560             case Audio::snd:
561                 fn = "sun audio";
562                 break;
563             case Audio::riff:
564                 fn = "riff";
565                 break;
566             case Audio::wave:
567                 fn = "ms wave";
568                 break;
569             case Audio::mpeg:
570                 fn = "mpeg audio";
571                 break;
572             }
573 
574 
575         if(fn)
576             printf("    %s: %s\n", _TEXT("Format"), fn);
577         else
578             printf("    %s: %s\n", _TEXT("Format"), _TEXT("unknown"));
579 
580         printf("    %s: %s\n", _TEXT("Encoding"), Audio::getName(info.encoding));
581         if(Audio::is_stereo(info.encoding))
582             printf("    %s: 2\n", _TEXT("Channels"));
583         else
584             printf("    %s: 1\n", _TEXT("Channels"));
585         if(info.framing)
586             printf("    %s: %ldms\n", _TEXT("Frame Size"), info.framing);
587         if(Audio::is_linear(info.encoding)) {
588             if(info.order == __BIG_ENDIAN)
589                 printf("    %s: %s\n", _TEXT("Byte Order"), _TEXT("big"));
590             else if(info.order == __LITTLE_ENDIAN)
591                 printf("    %s: %s\n", _TEXT("Byte Order"), _TEXT("little"));
592             else
593                 printf("    %s: %s\n", _TEXT("Byte Order"), _TEXT("native"));
594         }
595         printf("    %s: %ld\n", _TEXT("Sample Rate"), info.rate);
596         printf("    %s: %ld\n", _TEXT("Bit Rate"), info.bitrate);
597         printf("    %s: %ld\n", _TEXT("Samples"), end);
598 
599         scale = info.rate / 1000;
600 
601         subsec = (end % info.rate) / scale;
602 
603         end /= info.rate;
604         seconds = end % 60;
605         end /= 60;
606         minutes = end % 60;
607         end /= 60;
608         printf("    %s %02ld:%02ld:%02ld.%03ld\n",
609             _TEXT("Duration"), end, minutes, seconds, subsec);
610 
611         if(info.headersize)
612             printf("    %s: %u, %s=%u, %s=%u\n",
613                 _TEXT("Computed Frame Size"),
614                 info.framesize - info.headersize - info.padding,
615                 _TEXT("header"), info.headersize,
616                 _TEXT("padding"), info.padding);;
617 
618         au.close();
619     }
620     exit(0);
621 }
622 
strip(char ** argv)623 static void strip(char **argv)
624 {
625     AudioFile file, tmp;
626     Audio::Info info;
627     AudioCodec *codec = NULL;
628     char *fn;
629     timeout_t framing = 20;
630     short silence = 0;
631     int rtn;
632     unsigned char *buffer;
633     Audio::Level max, current;
634     unsigned long sum;
635     unsigned long count;
636     char target[256];
637 
638 retry:
639     if(!*argv) {
640         shell::errexit(2, "*** audiotool: -strip: %s\n",
641             _TEXT("missing arguments"));
642     }
643 
644     fn = *argv;
645 
646     if(eq(fn, "--")) {
647         ++argv;
648         goto skip;
649     }
650 
651     if(eq(fn, "--", 2))
652         ++fn;
653     if(eq(fn, "-framing=", 9)) {
654         framing = atoi(fn + 9);
655         ++argv;
656         goto retry;
657     }
658     else if(eq(fn, "-framing"))
659     {
660         ++argv;
661         if(!*argv) {
662             shell::errexit(2, "*** audiotool: -strip: -framing: %s\n",
663                 _TEXT("missing argument"));
664         }
665         framing = atoi(*(argv++));
666         goto retry;
667     }
668 
669     if(eq(fn, "-silence=", 9)) {
670         silence = atoi(fn + 9);
671         ++argv;
672         goto retry;
673     }
674     else if(eq(fn, "-silence"))
675     {
676         ++argv;
677         if(!*argv) {
678             shell::errexit(2, "*** audiotool: -strip: -silence: %s\n",
679                 _TEXT("missing argument"));
680         }
681         silence = atoi(*(argv++));
682         goto retry;
683     }
684 
685 skip:
686 
687     if(!framing)
688         framing = 20;
689 
690     while(*argv) {
691         if(!fsys::is_file(*argv)) {
692             printf("%s: %s\n",
693                 *(argv++), _TEXT("invalid"));
694             continue;
695         }
696         if(fsys::is_readable(*argv)) {
697             printf("%s: %s\n",
698                 *(argv++), _TEXT("inaccessable"));
699             continue;
700         }
701         rewrite(*argv, target, sizeof(target));
702         delfile = target;
703         file.open(*argv, Audio::modeRead, framing);
704         file.getInfo(&info);
705         if(!Audio::is_linear(info.encoding))
706             codec = AudioCodec::get(info);
707         if(!Audio::is_linear(info.encoding) && !codec) {
708             printf("%s: %s\n",
709                 *(argv++), _TEXT("cannot load codec"));
710             continue;
711         }
712 
713         buffer = new unsigned char[info.framesize];
714 
715         max = 0;
716         sum = 0;
717         count = 0;
718 
719         // compute silence value
720 
721         while(!silence) {
722             rtn = file.getBuffer(buffer, info.framesize);
723             if(rtn < (int)info.framesize)
724                 break;
725             ++count;
726             if(codec)
727                 sum += codec->impulse(buffer, info.framecount);
728             else
729                 sum += Audio::impulse(info, buffer, info.framecount);
730         }
731 
732         if(!silence && count)
733             silence = (Audio::Level)(((sum / count) * 2) / 3);
734 
735         max = 0;
736         sum = 0;
737         count = 0;
738 
739         file.setPosition(0);
740 
741 
742         tmp.create(target, &info);
743         if(!tmp.is_open()) {
744             printf("%s: %s\n",
745                 *(argv++), _TEXT("cannot rewrite"));
746             continue;
747         }
748 
749         for(;;)
750         {
751             rtn = file.getBuffer(buffer, info.framesize);
752             if(rtn < (int)info.framesize)
753                 break;
754             ++count;
755             if(codec) {
756                 if(codec->is_silent(silence, buffer, info.framecount)) {
757                     if(codec->peak(buffer, info.framecount) >= silence)
758                         tmp.putBuffer(buffer, info.framesize);
759                 }
760                 else
761                     tmp.putBuffer(buffer, info.framesize);
762                 continue;
763             }
764 
765             current = Audio::peak(info, buffer, info.framecount);
766             if(current > max)
767                 max = current;
768 
769             sum += Audio::impulse(info, buffer, info.framecount);
770             if(Audio::impulse(info, buffer, info.framecount) < silence) {
771                 if(Audio::peak(info, buffer, info.framecount) >= silence)
772                     tmp.putBuffer(buffer, info.framecount);
773             }
774             else
775                 tmp.putBuffer(buffer, info.framecount);
776         }
777         if(buffer)
778             delete[] buffer;
779 
780         if(codec)
781             AudioCodec::release(codec);
782 
783         codec = NULL;
784         buffer = NULL;
785 
786         file.close();
787         tmp.close();
788 
789         rtn = rename(target, *argv);
790         remove(target);
791         delfile = NULL;
792 
793         if(rtn)
794             printf("%s: %s\n",
795                 *argv, _TEXT("could not be replaced"));
796 
797         ++argv;
798     }
799     exit(0);
800 }
801 
trim(char ** argv)802 static void trim(char **argv)
803 {
804     AudioFile file, tmp;
805     unsigned long first = 0, last = 0, total = 0, padding = 0;
806     Audio::Info info;
807     AudioCodec *codec = NULL;
808     char *fn;
809     timeout_t framing = 20;
810     Audio::Level silence = 0;
811     int rtn;
812     unsigned char *buffer;
813     Audio::Linear samples = NULL;
814     unsigned long sum;
815     unsigned long count;
816     char target[256];
817     bool use = false;
818 
819 retry:
820     if(!*argv) {
821         shell::errexit(2, "*** audiotool: -trim: %s\n",
822             _TEXT("missing arguments"));
823         exit(-1);
824     }
825 
826     fn = *argv;
827 
828     if(eq(fn, "--")) {
829         ++argv;
830         goto skip;
831     }
832 
833     if(eq(fn, "--", 2))
834         ++fn;
835     if(eq(fn, "-framing=", 9)) {
836         framing = atoi(fn + 9);
837         ++argv;
838         goto retry;
839     }
840     else if(eq(fn, "-framing"))
841     {
842         ++argv;
843         if(!*argv) {
844             shell::errexit(3, "*** audiotool: -trim: -framing: %s\n",
845                 _TEXT("missing argument"));
846         }
847         framing = atoi(*(argv++));
848         goto retry;
849     }
850 
851     if(eq(fn, "-padding=", 9)) {
852         padding = atoi(fn + 9);
853         ++argv;
854         goto retry;
855     }
856     else if(eq(fn, "-padding"))
857     {
858         ++argv;
859         if(!*argv) {
860             shell::errexit(3, "*** audiotool: -trim: -padding: %s\n",
861                 _TEXT("missing argument"));
862         }
863         padding = atol(*(argv++));
864         goto retry;
865     }
866 
867     if(eq(fn, "-silence=", 9)) {
868         silence = atoi(fn + 9);
869         ++argv;
870         goto retry;
871     }
872     else if(eq(fn, "-silence"))
873     {
874         ++argv;
875         if(!*argv) {
876             shell::errexit(3, "*** audiotool: -trim: -silence: %s\n",
877                 _TEXT("missing argument"));
878         }
879         silence = atoi(*(argv++));
880         goto retry;
881     }
882 
883 skip:
884 
885     if(!framing)
886         framing = 20;
887 
888     while(*argv) {
889         if(!fsys::is_file(*argv)) {
890             printf("%s: %s\n",
891                 *(argv++), _TEXT("invalid"));
892             continue;
893         }
894         if(fsys::is_readable(*argv)) {
895             printf("%s: %s\n",
896                 *(argv++), _TEXT("inaccessable"));
897             continue;
898         }
899         rewrite(*argv, target, sizeof(target));
900         delfile = target;
901         file.open(*argv, Audio::modeRead, framing);
902         file.getInfo(&info);
903         if(!Audio::is_linear(info.encoding))
904             codec = AudioCodec::get(info);
905         if(!Audio::is_linear(info.encoding) && !codec) {
906             printf("%s: %s\n",
907                 *(argv++), _TEXT("cannot load codec"));
908             continue;
909         }
910 
911         buffer = new unsigned char[info.framesize];
912 
913         sum = 0;
914         count = 0;
915 
916         // compute silence value
917 
918         while(!silence) {
919             rtn = file.getBuffer(buffer, info.framesize);
920             if(rtn < (int)info.framesize)
921                 break;
922             ++count;
923             if(codec)
924                 sum += codec->impulse(buffer, info.framecount);
925             else
926                 sum += Audio::impulse(info, buffer, info.framecount);
927         }
928 
929         if(!silence && count)
930             silence = (Audio::Level)(((sum / count) * 2) / 3);
931 
932         sum = 0;
933         count = 0;
934 
935         file.setPosition(0);
936 
937         for(;;)
938         {
939             rtn = file.getBuffer(buffer, info.framesize);
940             if(rtn < (int)info.framesize)
941                 break;
942 
943             ++total;
944             if(codec) {
945                 if(codec->is_silent(silence, buffer, info.framecount)) {
946                     use = false;
947                     if(codec->peak(buffer, info.framecount) >= silence)
948                         use = true;
949                 }
950                 else
951                     use = true;
952             }
953             if(use && !first)
954                 first = total;
955             if(use)
956                 last = total;
957         }
958 
959         if(!last || !first) {
960             printf("%s: %s\n",
961                 *(argv++), _TEXT("all silent, skipping"));
962             continue;
963         }
964 
965         --first;
966         total = last - first;
967         file.setPosition(first * info.framecount);
968 
969         tmp.create(target, &info);
970         if(!tmp.is_open()) {
971             printf("%s: %s\n",
972                 *(argv++), _TEXT("cannot rewrite"));
973             continue;
974         }
975 
976         while(total--) {
977             rtn = file.getBuffer(buffer, info.framesize);
978             if(rtn < (int)info.framesize)
979                 break;
980             tmp.putBuffer(buffer, info.framesize);
981         }
982 
983         if(padding) {
984             if(!codec)
985                 memset(buffer, 0, info.framesize);
986             else if(Audio::is_stereo(info.encoding))
987             {
988                 samples = new Audio::Sample[info.framecount * 2];
989                 memset(samples, 0, info.framecount * 2);
990                 codec->encode(samples, buffer, info.framecount);
991             }
992             else {
993                 samples = new Audio::Sample[info.framecount];
994                 memset(samples, 0, info.framecount);
995                 codec->encode(samples, buffer, info.framecount);
996             }
997         }
998 
999         while(padding--) {
1000             rtn = tmp.putBuffer(buffer, info.framesize);
1001         }
1002 
1003         if(samples)
1004             delete[] samples;
1005 
1006         if(buffer)
1007             delete[] buffer;
1008 
1009         if(codec)
1010             AudioCodec::release(codec);
1011 
1012         codec = NULL;
1013         buffer = NULL;
1014 
1015         file.close();
1016         tmp.close();
1017 
1018         rtn = rename(target, *argv);
1019         remove(target);
1020         delfile = NULL;
1021 
1022         if(rtn)
1023             printf("%s: %s\n",
1024                 *argv, _TEXT("could not be replaced"));
1025 
1026         ++argv;
1027     }
1028     exit(0);
1029 }
1030 
size(char ** argv)1031 static void size(char **argv)
1032 {
1033     char *fn = *(argv++);
1034     AudioFile file;
1035     Audio::Info info;
1036     unsigned long pos;
1037 
1038     if(!fn) {
1039         shell::errexit(2, "*** audiotool: -size: %s\n",
1040             _TEXT("no file specified"));
1041     }
1042 
1043     file.open(fn, Audio::modeRead);
1044     if(!file.is_open()) {
1045         shell::errexit(3, "*** audiotool: %s: %s\n",
1046             fname(fn), _TEXT("cannot access"));
1047     }
1048     file.getInfo(&info);
1049     file.setPosition();
1050     pos = file.getPosition();
1051     pos /= info.rate;
1052     printf("%ld\n", pos);
1053     exit(0);
1054 }
1055 
play(char ** argv)1056 static void play(char **argv)
1057 {
1058     AudioDevice *dev;
1059     PlayStream playfile;
1060     const char *path = *argv;
1061     Audio::Linear buffer;
1062     Audio::Info info;
1063     unsigned bufcount, pages;
1064 
1065     dev = Audio::getDevice();
1066 
1067     if(!Audio::is_available() && !dev) {
1068         shell::errexit(10, "*** audiotool: %s\n",
1069             _TEXT("no device supported"));
1070     }
1071     else if(!dev) {
1072         shell::errexit(10, "*** audiotool: %s\n",
1073             _TEXT("device unavailable"));
1074     }
1075 
1076     playfile.open(argv);
1077 
1078     if(!playfile.is_open()) {
1079         shell::errexit(10, "*** audiotool: %s: %s\n",
1080             fname(path), _TEXT("unable to access"));
1081     }
1082 
1083     if(!playfile.is_streamable()) {
1084         shell::errexit(10, "*** audiotool: %s: %s\n",
1085             fname(path), _TEXT("missing codec"));
1086     }
1087 
1088     playfile.getInfo(&info);
1089     if(!dev->setAudio((Audio::Rate)info.rate, Audio::is_stereo(info.encoding), 10)) {
1090         shell::errexit(10, "*** audiotool: %s\n",
1091             _TEXT("sound device does not support rate"));
1092         exit(-1);
1093     }
1094 
1095     bufcount = playfile.getCount();
1096     if(Audio::is_stereo(info.encoding))
1097         buffer = new Audio::Sample[bufcount * 2];
1098     else
1099         buffer = new Audio::Sample[bufcount];
1100 
1101     for(;;) {
1102         if(Audio::is_stereo(info.encoding))
1103             pages = playfile.getStereo(buffer, 1);
1104         else
1105             pages = playfile.getMono(buffer, 1);
1106 
1107         if(!pages)
1108             break;
1109 
1110         dev->putSamples(buffer, bufcount);
1111     }
1112 
1113     dev->sync();
1114     delete dev;
1115     playfile.close();
1116     exit(0);
1117 }
1118 
packetdump(char ** argv)1119 static void packetdump(char **argv)
1120 {
1121     PacketStream packetfile;
1122     const char *path = *argv;
1123     Audio::Encoded buffer;
1124     Audio::Info info;
1125     ssize_t count;
1126 
1127     packetfile.open(argv);
1128 
1129     if(!packetfile.is_open()) {
1130         shell::errexit(2, "*** audiotool: %s: %s\n",
1131             fname(path), _TEXT("cannot access"));
1132     }
1133 
1134     if(!packetfile.is_streamable()) {
1135         shell::errexit(2, "*** audiotool: %s: %s\n",
1136             fname(path), _TEXT("missing codec needed"));
1137     }
1138 
1139     packetfile.getInfo(&info);
1140 
1141     buffer = new unsigned char[Audio::maxFramesize(info)];
1142 
1143     while((count = packetfile.getPacket(buffer)) > 0)
1144         printf("-- %ld\n", (long)count);
1145 
1146     delete[] buffer;
1147     packetfile.close();
1148     exit(0);
1149 }
1150 
1151 
note(char ** argv)1152 static void note(char **argv)
1153 {
1154     char *fn = *(argv++);
1155     char *ann = NULL;
1156     AudioFile file, tmp;
1157     Audio::Info info;
1158     char target[256];
1159     unsigned char buffer[4096];
1160     int rtn;
1161 
1162     if(!fn) {
1163         shell::errexit(2, "*** audiotool: -notation: %s\n",
1164             _TEXT("no file specified"));
1165     }
1166 
1167     ann = *argv;
1168 
1169     file.open(fn, Audio::modeRead);
1170     if(!file.is_open()) {
1171         shell::errexit(4, "*** audiotool: %s: %s\n:",
1172             fname(fn), _TEXT("cannot access"));
1173     }
1174     file.getInfo(&info);
1175     if(info.annotation && !ann)
1176         printf("%s\n", info.annotation);
1177     if(!ann)
1178         exit(0);
1179     rewrite(fn, target, sizeof(target));
1180     info.annotation = ann;
1181     delfile = target;
1182     remove(target);
1183     tmp.create(target, &info);
1184     if(!tmp.is_open()) {
1185         shell::errexit(5, "*** audiotool: %s: %s\n",
1186             fname(target), _TEXT("unable to create"));
1187     }
1188     for(;;) {
1189         rtn = file.getBuffer(buffer, sizeof(buffer));
1190         if(!rtn)
1191             break;
1192         if(rtn < 0) {
1193             remove(target);
1194             shell::errexit(6, "*** audiotool: %s: %s\n",
1195                 fname(fn), _TEXT("read failed"));
1196         }
1197         rtn = tmp.putBuffer(buffer, rtn);
1198         if(rtn < 1) {
1199             remove(target);
1200             shell::errexit(6, "*** audiotool: %s: %s\n",
1201                 fname(fn), _TEXT("write failed"));
1202         }
1203     }
1204     file.close();
1205     tmp.close();
1206     rtn = rename(target, fn);
1207     remove(target);
1208     delfile = NULL;
1209     if(rtn) {
1210         shell::errexit(6, "*** audiotool: %s: %s\n",
1211             fname(fn), _TEXT("could not replace"));
1212     }
1213 
1214     exit(0);
1215 }
1216 
build(char ** argv)1217 static void build(char **argv)
1218 {
1219     AudioBuild input;
1220     AudioStream output;
1221     Audio::Info info, make;
1222     const char *target = NULL;
1223     char *option;
1224     char *encoding = NULL;
1225     Audio::Rate rate = Audio::rateUnknown;
1226 
1227 retry:
1228     if(!*argv) {
1229         shell::errexit(2, "*** audiotool: -build: %s\n",
1230             _TEXT("missing arguments"));
1231     }
1232 
1233     option = *argv;
1234     if(eq("--", option)) {
1235         ++argv;
1236         goto skip;
1237     }
1238 
1239     if(eq("--", option, 2))
1240         ++option;
1241 
1242     if(eq(option, "-encoding=", 10)) {
1243         encoding = option + 10;
1244         ++argv;
1245         goto retry;
1246     }
1247 
1248     if(eq(option, "-rate=", 6)) {
1249         rate = (Audio::Rate)atol(option + 6);
1250         ++argv;
1251         goto retry;
1252     }
1253 
1254     if(eq(option, "-encoding")) {
1255         ++argv;
1256         if(!*argv) {
1257             shell::errexit(3, "*** audiotool: -build: -encoding: %s\n",
1258                 _TEXT("missing argument"));
1259         }
1260         encoding = *(argv++);
1261         goto retry;
1262     }
1263 
1264     if(eq(option, "-rate")) {
1265         ++argv;
1266         if(!*argv) {
1267             shell::errexit(3, "*** audiotool: -build: -rate: %s\n",
1268                 _TEXT("missing argument"));
1269         }
1270         rate = (Audio::Rate)atol(*(argv++));
1271         goto retry;
1272     }
1273 
1274 skip:
1275     if(*argv && **argv == '-') {
1276         shell::errexit(2, "*** auditool: -build: %s: %s\n",
1277             *argv, _TEXT("unknown option"));
1278     }
1279 
1280     if(*argv)
1281         target = *(argv++);
1282 
1283     if(!*argv) {
1284         shell::errexit(4, "*** audiotool: -build: %s\n",
1285             _TEXT("no files specified"));
1286     }
1287 
1288     input.open(argv);
1289     if(!input.is_open()) {
1290         shell::errexit(4, "*** audiotool: %s: %s\n",
1291             *argv, _TEXT("cannot access"));
1292     }
1293     input.getInfo(&info);
1294     input.getInfo(&make);
1295     if(target)
1296         remove(target);
1297     if(encoding)
1298         make.encoding = Audio::getEncoding(encoding);
1299 
1300     if(rate != Audio::rateUnknown)
1301         make.setRate(rate);
1302 
1303     if(target)
1304         output.create(target, &make, false, 10);
1305     if(!output.is_open()) {
1306         shell::errexit(5, "*** audiotool: %s: %s\n",
1307             target, _TEXT("cannot create"));
1308     }
1309     output.getInfo(&make);
1310 
1311     if(make.encoding == info.encoding && make.rate == info.rate)
1312         AudioBuild::copyDirect(input, output);
1313     else {
1314         if(!input.is_streamable()) {
1315             remove(target);
1316             shell::errexit(6, "*** audiotool: %s: %s\n",
1317                 *argv, _TEXT("cannot load codec"));
1318         }
1319         if(!output.is_streamable()) {
1320             remove(target);
1321             shell::errexit(6, "*** audiotool: %s: %s\n",
1322                 target, _TEXT("cannot load codec"));
1323         }
1324         AudioBuild::copyConvert(input, output);
1325     }
1326     input.close();
1327     output.close();
1328     exit(0);
1329 }
1330 
append(char ** argv)1331 static void append(char **argv)
1332 {
1333     AudioBuild input;
1334     AudioStream output;
1335     Audio::Info info, make;
1336     const char *target = NULL;
1337     char *option;
1338     char *offset = NULL;
1339 
1340 retry:
1341     option = *argv;
1342 
1343     if(eq("--", option)) {
1344         ++argv;
1345         goto skip;
1346     }
1347 
1348     if(eq("--", option, 2))
1349         ++option;
1350 
1351     if(eq(option, "-offset=", 8)) {
1352         offset = option + 8;
1353         ++argv;
1354         goto retry;
1355     }
1356 
1357     if(eq(option, "-offset")) {
1358         ++argv;
1359         if(!*argv) {
1360             shell::errexit(3, "*** audiotool: -append: -offset: %s\n",
1361                 _TEXT("missing argument"));
1362             exit(-1);
1363         }
1364         offset = *(argv++);
1365         goto retry;
1366     }
1367 
1368 skip:
1369     if(*argv && **argv == '-') {
1370         shell::errexit(2, "*** auditool: -append: %s: %s\n",
1371             *argv, _TEXT("unknown option"));
1372     }
1373 
1374     if(*argv)
1375         target = *(argv++);
1376 
1377     if(!*argv) {
1378         shell::errexit(4, "*** audiotool: -append: %s\n",
1379             _TEXT("no files specified"));
1380     }
1381 
1382     input.open(argv);
1383     if(!input.is_open()) {
1384         shell::errexit(4, "*** audiotool: %s: %s\n",
1385             *argv, _TEXT("cannot access"));
1386     }
1387     input.getInfo(&info);
1388     output.open(target, Audio::modeWrite, 10);
1389     if(!output.is_open()) {
1390         shell::errexit(4, "*** audiotool: %s: %s\n",
1391             target, _TEXT("cannot access"));
1392     }
1393     output.getInfo(&make);
1394 
1395     if(offset)
1396         output.setPosition(atol(offset));
1397     else
1398         output.setPosition();
1399 
1400     if(make.encoding == info.encoding)
1401         AudioBuild::copyDirect(input, output);
1402     else {
1403         if(!input.is_streamable()) {
1404             shell::errexit(6, "*** audiotool: %s: %s\n",
1405                 *argv, _TEXT("cannot load codec"));
1406         }
1407         if(!output.is_streamable()) {
1408             shell::errexit(6, "*** audiotool: %s: %s\n",
1409                 target, _TEXT("cannot load codec"));
1410         }
1411         AudioBuild::copyConvert(input, output);
1412     }
1413     input.close();
1414     output.close();
1415     exit(0);
1416 }
1417 
PROGRAM_MAIN(argc,argv)1418 PROGRAM_MAIN(argc, argv)
1419 {
1420     char *cp;
1421 
1422     if(argc < 2) {
1423         shell::errexit(1, "%s\n",
1424             _TEXT("use: audiotool -command [-options...] [args...]"));
1425     }
1426     ++argv;
1427     cp = *argv;
1428     while(*cp == '-')
1429         ++cp;
1430 
1431     shell::exiting(stop);
1432     Audio::init();
1433 
1434     if(eq(cp, "version"))
1435         version();
1436     else if(eq(cp, "endian"))
1437         showendian();
1438     else if(eq(cp, "soundcard")) {
1439         if(*(++argv))
1440             soundcard(atoi(*argv));
1441         else
1442             soundcard(0);
1443     }
1444     else if(eq(cp, "build"))
1445         build(++argv);
1446     else if(eq(cp, "append"))
1447         append(++argv);
1448     else if(eq(cp, "chart"))
1449         chart(++argv);
1450     else if(eq(cp, "info"))
1451         info(++argv);
1452     else if(eq(cp, "note") || eq(cp, "annotate") || eq(cp, "notation"))
1453         note(++argv);
1454     else if(eq(cp, "packets") || eq(cp, "dump") || eq(cp, "packetdump"))
1455         packetdump(++argv);
1456     else if(eq(cp, "play"))
1457         play(++argv);
1458     else if(eq(cp, "strip"))
1459         strip(++argv);
1460     else if(eq(cp, "trim"))
1461         trim(++argv);
1462     else if(eq(cp, "size"))
1463         size(++argv);
1464     else if(eq(cp, "codecs"))
1465         codecs();
1466     else if(eq(cp, "plugins"))
1467         plugins();
1468 
1469     shell::errexit(2, "*** audiotool: %s: %s\n",
1470         *argv, _TEXT("unknown option"));
1471     PROGRAM_EXIT(0);
1472 }
1473