eb0e4fba8e914c8064c13cec80bb55321b13c225
[blender.git] / intern / audaspace / intern / AUD_C-API.cpp
1 /*
2  * $Id$
3  *
4  * ***** BEGIN LGPL LICENSE BLOCK *****
5  *
6  * Copyright 2009 Jörg Hermann Müller
7  *
8  * This file is part of AudaSpace.
9  *
10  * AudaSpace is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * AudaSpace is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with AudaSpace.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  * ***** END LGPL LICENSE BLOCK *****
24  */
25
26 #ifdef WITH_PYTHON
27 #include "AUD_PyAPI.h"
28
29 Device* g_device;
30 bool g_pyinitialized = false;
31 #endif
32
33 #include <cstdlib>
34 #include <cstring>
35 #include <cmath>
36
37 #ifndef __STDC_CONSTANT_MACROS
38 // needed for INT64_C
39 #define __STDC_CONSTANT_MACROS
40 #endif
41
42 #include "AUD_NULLDevice.h"
43 #include "AUD_I3DDevice.h"
44 #include "AUD_FileFactory.h"
45 #include "AUD_StreamBufferFactory.h"
46 #include "AUD_DelayFactory.h"
47 #include "AUD_LimiterFactory.h"
48 #include "AUD_PingPongFactory.h"
49 #include "AUD_LoopFactory.h"
50 #include "AUD_RectifyFactory.h"
51 #include "AUD_EnvelopeFactory.h"
52 #include "AUD_LinearResampleFactory.h"
53 #include "AUD_LowpassFactory.h"
54 #include "AUD_HighpassFactory.h"
55 #include "AUD_AccumulatorFactory.h"
56 #include "AUD_SumFactory.h"
57 #include "AUD_SquareFactory.h"
58 #include "AUD_ChannelMapperFactory.h"
59 #include "AUD_Buffer.h"
60 #include "AUD_ReadDevice.h"
61 #include "AUD_SourceCaps.h"
62 #include "AUD_IReader.h"
63 #include "AUD_SequencerFactory.h"
64
65 #ifdef WITH_SDL
66 #include "AUD_SDLDevice.h"
67 #endif
68
69 #ifdef WITH_OPENAL
70 #include "AUD_OpenALDevice.h"
71 #endif
72
73 #ifdef WITH_JACK
74 #include "AUD_JackDevice.h"
75 #endif
76
77
78 #ifdef WITH_FFMPEG
79 extern "C" {
80 #include <libavformat/avformat.h>
81 }
82 #endif
83
84 #include <cassert>
85
86 typedef AUD_IFactory AUD_Sound;
87 typedef AUD_ReadDevice AUD_Device;
88 typedef AUD_Handle AUD_Channel;
89
90 #define AUD_CAPI_IMPLEMENTATION
91 #include "AUD_C-API.h"
92
93 #ifndef NULL
94 #define NULL 0
95 #endif
96
97 static AUD_IDevice* AUD_device = NULL;
98 static int AUD_available_devices[4];
99 static AUD_I3DDevice* AUD_3ddevice = NULL;
100
101 void AUD_initOnce()
102 {
103 #ifdef WITH_FFMPEG
104         av_register_all();
105 #endif
106 }
107
108 int AUD_init(AUD_DeviceType device, AUD_DeviceSpecs specs, int buffersize)
109 {
110         AUD_IDevice* dev = NULL;
111
112         if(AUD_device)
113                 AUD_exit();
114
115         try
116         {
117                 switch(device)
118                 {
119                 case AUD_NULL_DEVICE:
120                         dev = new AUD_NULLDevice();
121                         break;
122 #ifdef WITH_SDL
123                 case AUD_SDL_DEVICE:
124                         dev = new AUD_SDLDevice(specs, buffersize);
125                         break;
126 #endif
127 #ifdef WITH_OPENAL
128                 case AUD_OPENAL_DEVICE:
129                         dev = new AUD_OpenALDevice(specs, buffersize);
130                         break;
131 #endif
132 #ifdef WITH_JACK
133                 case AUD_JACK_DEVICE:
134                         dev = new AUD_JackDevice(specs, buffersize);
135                         break;
136 #endif
137                 default:
138                         return false;
139                 }
140
141                 AUD_device = dev;
142                 if(AUD_device->checkCapability(AUD_CAPS_3D_DEVICE))
143                         AUD_3ddevice = dynamic_cast<AUD_I3DDevice*>(AUD_device);
144
145 #ifdef WITH_PYTHON
146                 if(g_pyinitialized)
147                 {
148                         g_device = (Device*)Device_empty();
149                         if(g_device != NULL)
150                         {
151                                 g_device->device = dev;
152                         }
153                 }
154 #endif
155
156                 return true;
157         }
158         catch(AUD_Exception)
159         {
160                 return false;
161         }
162 }
163
164 int* AUD_enumDevices()
165 {
166         int i = 0;
167 #ifdef WITH_SDL
168         AUD_available_devices[i++] = AUD_SDL_DEVICE;
169 #endif
170 #ifdef WITH_OPENAL
171         AUD_available_devices[i++] = AUD_OPENAL_DEVICE;
172 #endif
173 #ifdef WITH_JACK
174         AUD_available_devices[i++] = AUD_JACK_DEVICE;
175 #endif
176         AUD_available_devices[i++] = AUD_NULL_DEVICE;
177         return AUD_available_devices;
178 }
179
180 void AUD_exit()
181 {
182 #ifdef WITH_PYTHON
183         if(g_device)
184         {
185                 Py_XDECREF(g_device);
186                 g_device = NULL;
187         }
188         else
189 #endif
190         if(AUD_device)
191                 delete AUD_device;
192         AUD_device = NULL;
193         AUD_3ddevice = NULL;
194 }
195
196 #ifdef WITH_PYTHON
197 static PyObject* AUD_getCDevice(PyObject* self)
198 {
199         if(g_device)
200         {
201                 Py_INCREF(g_device);
202                 return (PyObject*)g_device;
203         }
204         Py_RETURN_NONE;
205 }
206
207 static PyMethodDef meth_getcdevice[] = {{ "device", (PyCFunction)AUD_getCDevice, METH_NOARGS,
208                                                                                   "device()\n\n"
209                                                                                   "Returns the application's Device.\n\n"
210                                                                                   ":return: The application's Device.\n"
211                                                                                   ":rtype: aud.Device"}};
212
213 PyObject* AUD_initPython()
214 {
215         PyObject* module = PyInit_aud();
216         PyModule_AddObject(module, "device", (PyObject *)PyCFunction_New(meth_getcdevice, NULL));
217         PyDict_SetItemString(PySys_GetObject("modules"), "aud", module);
218         if(AUD_device)
219         {
220                 g_device = (Device*)Device_empty();
221                 if(g_device != NULL)
222                 {
223                         g_device->device = AUD_device;
224                 }
225         }
226         g_pyinitialized = true;
227
228         return module;
229 }
230 #endif
231
232 void AUD_lock()
233 {
234         assert(AUD_device);
235         AUD_device->lock();
236 }
237
238 void AUD_unlock()
239 {
240         assert(AUD_device);
241         AUD_device->unlock();
242 }
243
244 AUD_SoundInfo AUD_getInfo(AUD_Sound* sound)
245 {
246         assert(sound);
247
248         AUD_IReader* reader = sound->createReader();
249
250         AUD_SoundInfo info;
251
252         if(reader)
253         {
254                 info.specs = reader->getSpecs();
255                 info.length = reader->getLength() / (float) info.specs.rate;
256         }
257         else
258         {
259                 info.specs.channels = AUD_CHANNELS_INVALID;
260                 info.specs.rate = AUD_RATE_INVALID;
261                 info.length = 0.0f;
262         }
263
264         return info;
265 }
266
267 AUD_Sound* AUD_load(const char* filename)
268 {
269         assert(filename);
270         return new AUD_FileFactory(filename);
271 }
272
273 AUD_Sound* AUD_loadBuffer(unsigned char* buffer, int size)
274 {
275         assert(buffer);
276         return new AUD_FileFactory(buffer, size);
277 }
278
279 AUD_Sound* AUD_bufferSound(AUD_Sound* sound)
280 {
281         assert(sound);
282
283         try
284         {
285                 return new AUD_StreamBufferFactory(sound);
286         }
287         catch(AUD_Exception)
288         {
289                 return NULL;
290         }
291 }
292
293 AUD_Sound* AUD_delaySound(AUD_Sound* sound, float delay)
294 {
295         assert(sound);
296
297         try
298         {
299                 return new AUD_DelayFactory(sound, delay);
300         }
301         catch(AUD_Exception)
302         {
303                 return NULL;
304         }
305 }
306
307 AUD_Sound* AUD_limitSound(AUD_Sound* sound, float start, float end)
308 {
309         assert(sound);
310
311         try
312         {
313                 return new AUD_LimiterFactory(sound, start, end);
314         }
315         catch(AUD_Exception)
316         {
317                 return NULL;
318         }
319 }
320
321 AUD_Sound* AUD_pingpongSound(AUD_Sound* sound)
322 {
323         assert(sound);
324
325         try
326         {
327                 return new AUD_PingPongFactory(sound);
328         }
329         catch(AUD_Exception)
330         {
331                 return NULL;
332         }
333 }
334
335 AUD_Sound* AUD_loopSound(AUD_Sound* sound)
336 {
337         assert(sound);
338
339         try
340         {
341                 return new AUD_LoopFactory(sound);
342         }
343         catch(AUD_Exception)
344         {
345                 return NULL;
346         }
347 }
348
349 int AUD_setLoop(AUD_Channel* handle, int loops, float time)
350 {
351         if(handle)
352         {
353                 AUD_Message message;
354                 message.type = AUD_MSG_LOOP;
355                 message.loopcount = loops;
356                 message.time = time;
357
358                 try
359                 {
360                         return AUD_device->sendMessage(handle, message);
361                 }
362                 catch(AUD_Exception)
363                 {
364                 }
365         }
366         return false;
367 }
368
369 AUD_Sound* AUD_rectifySound(AUD_Sound* sound)
370 {
371         assert(sound);
372
373         try
374         {
375                 return new AUD_RectifyFactory(sound);
376         }
377         catch(AUD_Exception)
378         {
379                 return NULL;
380         }
381 }
382
383 void AUD_unload(AUD_Sound* sound)
384 {
385         assert(sound);
386         delete sound;
387 }
388
389 AUD_Channel* AUD_play(AUD_Sound* sound, int keep)
390 {
391         assert(AUD_device);
392         assert(sound);
393         try
394         {
395                 return AUD_device->play(sound, keep);
396         }
397         catch(AUD_Exception)
398         {
399                 return NULL;
400         }
401 }
402
403 int AUD_pause(AUD_Channel* handle)
404 {
405         assert(AUD_device);
406         return AUD_device->pause(handle);
407 }
408
409 int AUD_resume(AUD_Channel* handle)
410 {
411         assert(AUD_device);
412         return AUD_device->resume(handle);
413 }
414
415 int AUD_stop(AUD_Channel* handle)
416 {
417         if(AUD_device)
418                 return AUD_device->stop(handle);
419         return false;
420 }
421
422 int AUD_setKeep(AUD_Channel* handle, int keep)
423 {
424         assert(AUD_device);
425         return AUD_device->setKeep(handle, keep);
426 }
427
428 int AUD_seek(AUD_Channel* handle, float seekTo)
429 {
430         assert(AUD_device);
431         return AUD_device->seek(handle, seekTo);
432 }
433
434 float AUD_getPosition(AUD_Channel* handle)
435 {
436         assert(AUD_device);
437         return AUD_device->getPosition(handle);
438 }
439
440 AUD_Status AUD_getStatus(AUD_Channel* handle)
441 {
442         assert(AUD_device);
443         return AUD_device->getStatus(handle);
444 }
445
446 AUD_Channel* AUD_play3D(AUD_Sound* sound, int keep)
447 {
448         assert(AUD_device);
449         assert(sound);
450
451         try
452         {
453                 if(AUD_3ddevice)
454                         return AUD_3ddevice->play3D(sound, keep);
455                 else
456                         return AUD_device->play(sound, keep);
457         }
458         catch(AUD_Exception)
459         {
460                 return NULL;
461         }
462 }
463
464 int AUD_updateListener(AUD_3DData* data)
465 {
466         assert(AUD_device);
467         assert(data);
468
469         try
470         {
471                 if(AUD_3ddevice)
472                         return AUD_3ddevice->updateListener(*data);
473         }
474         catch(AUD_Exception)
475         {
476         }
477         return false;
478 }
479
480 int AUD_set3DSetting(AUD_3DSetting setting, float value)
481 {
482         assert(AUD_device);
483
484         try
485         {
486                 if(AUD_3ddevice)
487                         return AUD_3ddevice->setSetting(setting, value);
488         }
489         catch(AUD_Exception)
490         {
491         }
492         return false;
493 }
494
495 float AUD_get3DSetting(AUD_3DSetting setting)
496 {
497         assert(AUD_device);
498
499         try
500         {
501                 if(AUD_3ddevice)
502                         return AUD_3ddevice->getSetting(setting);
503         }
504         catch(AUD_Exception)
505         {
506         }
507         return 0.0f;
508 }
509
510 int AUD_update3DSource(AUD_Channel* handle, AUD_3DData* data)
511 {
512         if(handle)
513         {
514                 assert(AUD_device);
515                 assert(data);
516
517                 try
518                 {
519                         if(AUD_3ddevice)
520                                 return AUD_3ddevice->updateSource(handle, *data);
521                 }
522                 catch(AUD_Exception)
523                 {
524                 }
525         }
526         return false;
527 }
528
529 int AUD_set3DSourceSetting(AUD_Channel* handle,
530                                                    AUD_3DSourceSetting setting, float value)
531 {
532         if(handle)
533         {
534                 assert(AUD_device);
535
536                 try
537                 {
538                         if(AUD_3ddevice)
539                                 return AUD_3ddevice->setSourceSetting(handle, setting, value);
540                 }
541                 catch(AUD_Exception)
542                 {
543                 }
544         }
545         return false;
546 }
547
548 float AUD_get3DSourceSetting(AUD_Channel* handle, AUD_3DSourceSetting setting)
549 {
550         if(handle)
551         {
552                 assert(AUD_device);
553
554                 try
555                 {
556                         if(AUD_3ddevice)
557                                 return AUD_3ddevice->getSourceSetting(handle, setting);
558                 }
559                 catch(AUD_Exception)
560                 {
561                 }
562         }
563         return 0.0f;
564 }
565
566 int AUD_setSoundVolume(AUD_Channel* handle, float volume)
567 {
568         if(handle)
569         {
570                 assert(AUD_device);
571                 AUD_SourceCaps caps;
572                 caps.handle = handle;
573                 caps.value = volume;
574
575                 try
576                 {
577                         return AUD_device->setCapability(AUD_CAPS_SOURCE_VOLUME, &caps);
578                 }
579                 catch(AUD_Exception) {}
580         }
581         return false;
582 }
583
584 int AUD_setSoundPitch(AUD_Channel* handle, float pitch)
585 {
586         if(handle)
587         {
588                 assert(AUD_device);
589                 AUD_SourceCaps caps;
590                 caps.handle = handle;
591                 caps.value = pitch;
592
593                 try
594                 {
595                         return AUD_device->setCapability(AUD_CAPS_SOURCE_PITCH, &caps);
596                 }
597                 catch(AUD_Exception) {}
598         }
599         return false;
600 }
601
602 AUD_Device* AUD_openReadDevice(AUD_DeviceSpecs specs)
603 {
604         try
605         {
606                 return new AUD_ReadDevice(specs);
607         }
608         catch(AUD_Exception)
609         {
610                 return NULL;
611         }
612 }
613
614 AUD_Channel* AUD_playDevice(AUD_Device* device, AUD_Sound* sound, float seek)
615 {
616         assert(device);
617         assert(sound);
618
619         try
620         {
621                 AUD_Channel* handle = device->play(sound);
622                 device->seek(handle, seek);
623                 return handle;
624         }
625         catch(AUD_Exception)
626         {
627                 return NULL;
628         }
629 }
630
631 int AUD_setDeviceVolume(AUD_Device* device, float volume)
632 {
633         assert(device);
634
635         try
636         {
637                 return device->setCapability(AUD_CAPS_VOLUME, &volume);
638         }
639         catch(AUD_Exception) {}
640         
641         return false;
642 }
643
644 int AUD_setDeviceSoundVolume(AUD_Device* device, AUD_Channel* handle,
645                                                          float volume)
646 {
647         if(handle)
648         {
649                 assert(device);
650                 AUD_SourceCaps caps;
651                 caps.handle = handle;
652                 caps.value = volume;
653
654                 try
655                 {
656                         return device->setCapability(AUD_CAPS_SOURCE_VOLUME, &caps);
657                 }
658                 catch(AUD_Exception) {}
659         }
660         return false;
661 }
662
663 int AUD_readDevice(AUD_Device* device, data_t* buffer, int length)
664 {
665         assert(device);
666         assert(buffer);
667
668         try
669         {
670                 return device->read(buffer, length);
671         }
672         catch(AUD_Exception)
673         {
674                 return false;
675         }
676 }
677
678 void AUD_closeReadDevice(AUD_Device* device)
679 {
680         assert(device);
681
682         try
683         {
684                 delete device;
685         }
686         catch(AUD_Exception)
687         {
688         }
689 }
690
691 float* AUD_readSoundBuffer(const char* filename, float low, float high,
692                                                    float attack, float release, float threshold,
693                                                    int accumulate, int additive, int square,
694                                                    float sthreshold, int samplerate, int* length)
695 {
696         AUD_Buffer buffer;
697         AUD_DeviceSpecs specs;
698         specs.channels = AUD_CHANNELS_MONO;
699         specs.rate = (AUD_SampleRate)samplerate;
700         AUD_Sound* sound;
701
702         AUD_FileFactory file(filename);
703         AUD_ChannelMapperFactory mapper(&file, specs);
704         AUD_LowpassFactory lowpass(&mapper, high);
705         AUD_HighpassFactory highpass(&lowpass, low);
706         AUD_EnvelopeFactory envelope(&highpass, attack, release, threshold, 0.1f);
707         AUD_LinearResampleFactory resampler(&envelope, specs);
708         sound = &resampler;
709         AUD_SquareFactory squaref(sound, sthreshold);
710         if(square)
711                 sound = &squaref;
712         AUD_AccumulatorFactory accumulator(sound, additive);
713         AUD_SumFactory sum(sound);
714         if(accumulate)
715                 sound = &accumulator;
716         else if(additive)
717                 sound = &sum;
718
719         AUD_IReader* reader = sound->createReader();
720
721         if(reader == NULL)
722                 return NULL;
723
724         int len;
725         int position = 0;
726         sample_t* readbuffer;
727         do
728         {
729                 len = samplerate;
730                 buffer.resize((position + len) * sizeof(float), true);
731                 reader->read(len, readbuffer);
732                 memcpy(buffer.getBuffer() + position, readbuffer, len * sizeof(float));
733                 position += len;
734         } while(len != 0);
735         delete reader;
736
737         float* result = (float*)malloc(position * sizeof(float));
738         memcpy(result, buffer.getBuffer(), position * sizeof(float));
739         *length = position;
740         return result;
741 }
742
743 AUD_Sound* AUD_createSequencer(void* data, AUD_volumeFunction volume)
744 {
745 /* AUD_XXX should be this: but AUD_createSequencer is called before the device
746  * is initialized.
747
748         return new AUD_SequencerFactory(AUD_device->getSpecs().specs, data, volume);
749 */
750         AUD_Specs specs;
751         specs.channels = AUD_CHANNELS_STEREO;
752         specs.rate = AUD_RATE_44100;
753         return new AUD_SequencerFactory(specs, data, volume);
754 }
755
756 void AUD_destroySequencer(AUD_Sound* sequencer)
757 {
758         delete ((AUD_SequencerFactory*)sequencer);
759 }
760
761 AUD_SequencerEntry* AUD_addSequencer(AUD_Sound** sequencer, AUD_Sound* sound,
762                                                                  float begin, float end, float skip, void* data)
763 {
764         return ((AUD_SequencerFactory*)sequencer)->add((AUD_IFactory**) sound, begin, end, skip, data);
765 }
766
767 void AUD_removeSequencer(AUD_Sound* sequencer, AUD_SequencerEntry* entry)
768 {
769         ((AUD_SequencerFactory*)sequencer)->remove(entry);
770 }
771
772 void AUD_moveSequencer(AUD_Sound* sequencer, AUD_SequencerEntry* entry,
773                                    float begin, float end, float skip)
774 {
775         ((AUD_SequencerFactory*)sequencer)->move(entry, begin, end, skip);
776 }
777
778 void AUD_muteSequencer(AUD_Sound* sequencer, AUD_SequencerEntry* entry, char mute)
779 {
780         ((AUD_SequencerFactory*)sequencer)->mute(entry, mute);
781 }
782
783 int AUD_readSound(AUD_Sound* sound, sample_t* buffer, int length)
784 {
785         AUD_IReader* reader = sound->createReader();
786         AUD_DeviceSpecs specs;
787         sample_t* buf;
788
789         specs.specs = reader->getSpecs();
790         specs.channels = AUD_CHANNELS_MONO;
791         specs.format = AUD_FORMAT_FLOAT32;
792
793         AUD_ChannelMapperFactory mapper(reader, specs);
794
795         if(!reader || reader->getType() != AUD_TYPE_BUFFER)
796                 return -1;
797
798         reader = mapper.createReader();
799
800         if(!reader)
801                 return -1;
802
803         int len = reader->getLength();
804         float samplejump = (float)len / (float)length;
805         float min, max;
806
807         for(int i = 0; i < length; i++)
808         {
809                 len = floor(samplejump * (i+1)) - floor(samplejump * i);
810                 reader->read(len, buf);
811
812                 if(len < 1)
813                 {
814                         length = i;
815                         break;
816                 }
817
818                 max = min = *buf;
819                 for(int j = 1; j < len; j++)
820                 {
821                         if(buf[j] < min)
822                                 min = buf[j];
823                         if(buf[j] > max)
824                                 max = buf[j];
825                         buffer[i * 2] = min;
826                         buffer[i * 2 + 1] = max;
827                 }
828         }
829
830         delete reader; AUD_DELETE("reader")
831
832         return length;
833 }
834
835 void AUD_startPlayback()
836 {
837 #ifdef WITH_JACK
838         AUD_JackDevice* device = dynamic_cast<AUD_JackDevice*>(AUD_device);
839         if(device)
840                 device->startPlayback();
841 #endif
842 }
843
844 void AUD_stopPlayback()
845 {
846 #ifdef WITH_JACK
847         AUD_JackDevice* device = dynamic_cast<AUD_JackDevice*>(AUD_device);
848         if(device)
849                 device->stopPlayback();
850 #endif
851 }
852
853 void AUD_seekSequencer(AUD_Channel* handle, float time)
854 {
855 #ifdef WITH_JACK
856         AUD_JackDevice* device = dynamic_cast<AUD_JackDevice*>(AUD_device);
857         if(device)
858                 device->seekPlayback(time);
859         else
860 #endif
861         {
862                 AUD_device->seek(handle, time);
863         }
864 }
865
866 float AUD_getSequencerPosition(AUD_Channel* handle)
867 {
868 #ifdef WITH_JACK
869         AUD_JackDevice* device = dynamic_cast<AUD_JackDevice*>(AUD_device);
870         if(device)
871                 return device->getPlaybackPosition();
872         else
873 #endif
874         {
875                 return AUD_device->getPosition(handle);
876         }
877 }
878
879 #ifdef WITH_JACK
880 void AUD_setSyncCallback(AUD_syncFunction function, void* data)
881 {
882         AUD_JackDevice* device = dynamic_cast<AUD_JackDevice*>(AUD_device);
883         if(device)
884                 device->setSyncCallback(function, data);
885 }
886 #endif
887
888 int AUD_doesPlayback()
889 {
890 #ifdef WITH_JACK
891         AUD_JackDevice* device = dynamic_cast<AUD_JackDevice*>(AUD_device);
892         if(device)
893                 return device->doesPlayback();
894 #endif
895         return -1;
896 }
897
898 #ifdef AUD_DEBUG_MEMORY
899 int AUD_References(int count, const char* text)
900 {
901         static int m_count = 0;
902         m_count += count;
903         if(count > 0)
904                 printf("+%s\n", text);
905         if(count < 0)
906                 printf("-%s\n", text);
907         return m_count;
908 }
909 #endif