123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- /* -*- linux-c -*-
- Copyright (C) 2004 Tom Szilagyi
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: tap_echo.c,v 1.7 2004/12/06 09:32:41 tszilagyi Exp $
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <math.h>
- #include <ladspa.h>
- #include "tap_utils.h"
- /* The Unique ID of the plugin: */
- #define ID_STEREO 2143
- /* The port numbers for the plugin: */
- #define DELAYTIME_L 0
- #define FEEDBACK_L 1
- #define DELAYTIME_R 2
- #define FEEDBACK_R 3
- #define STRENGTH_L 4
- #define STRENGTH_R 5
- #define DRYLEVEL 6
- #define MODE 7
- #define HAAS 8
- #define REV_OUTCH 9
- #define INPUT_L 10
- #define OUTPUT_L 11
- #define INPUT_R 12
- #define OUTPUT_R 13
- /* Total number of ports */
- #define PORTCOUNT_STEREO 14
- /* Maximum delay (ms) */
- #define MAX_DELAY 2000
- /* The structure used to hold port connection information and state */
- typedef struct {
- LADSPA_Data * delaytime_L;
- LADSPA_Data * delaytime_R;
- LADSPA_Data * feedback_L;
- LADSPA_Data * feedback_R;
- LADSPA_Data * strength_L;
- LADSPA_Data * strength_R;
- LADSPA_Data * drylevel;
- LADSPA_Data * mode;
- LADSPA_Data * haas;
- LADSPA_Data * rev_outch;
- LADSPA_Data * input_L;
- LADSPA_Data * output_L;
- LADSPA_Data * input_R;
- LADSPA_Data * output_R;
- unsigned long sample_rate;
- LADSPA_Data mpx_out_L;
- LADSPA_Data mpx_out_R;
- LADSPA_Data * ringbuffer_L;
- LADSPA_Data * ringbuffer_R;
- unsigned long * buffer_pos_L;
- unsigned long * buffer_pos_R;
- LADSPA_Data run_adding_gain;
- } Echo;
- /* Construct a new plugin instance. */
- LADSPA_Handle
- instantiate_Echo(const LADSPA_Descriptor * Descriptor,
- unsigned long SampleRate) {
-
- LADSPA_Handle * ptr;
-
- if ((ptr = malloc(sizeof(Echo))) != NULL) {
- ((Echo *)ptr)->sample_rate = SampleRate;
- ((Echo *)ptr)->run_adding_gain = 1.0f;
- /* allocate memory for ringbuffers and related dynamic vars */
- if ((((Echo *)ptr)->ringbuffer_L =
- calloc(MAX_DELAY * ((Echo *)ptr)->sample_rate / 1000,
- sizeof(LADSPA_Data))) == NULL)
- exit(1);
- if ((((Echo *)ptr)->ringbuffer_R =
- calloc(MAX_DELAY * ((Echo *)ptr)->sample_rate / 1000,
- sizeof(LADSPA_Data))) == NULL)
- exit(1);
- if ((((Echo *)ptr)->buffer_pos_L = calloc(1, sizeof(unsigned long))) == NULL)
- exit(1);
- if ((((Echo *)ptr)->buffer_pos_R = calloc(1, sizeof(unsigned long))) == NULL)
- exit(1);
-
- *(((Echo *)ptr)->buffer_pos_L) = 0;
- *(((Echo *)ptr)->buffer_pos_R) = 0;
-
- return ptr;
- }
-
- return NULL;
- }
- /* activate a plugin instance */
- void
- activate_Echo(LADSPA_Handle Instance) {
- Echo * ptr = (Echo *)Instance;
- unsigned int i;
-
- ptr->mpx_out_L = 0;
- ptr->mpx_out_R = 0;
-
- *(ptr->buffer_pos_L) = 0;
- *(ptr->buffer_pos_R) = 0;
- for (i = 0; i < MAX_DELAY * ptr->sample_rate / 1000; i++) {
- ptr->ringbuffer_L[i] = 0.0f;
- ptr->ringbuffer_R[i] = 0.0f;
- }
- }
- /* Connect a port to a data location. */
- void
- connect_port_Echo(LADSPA_Handle Instance,
- unsigned long Port,
- LADSPA_Data * DataLocation) {
-
- Echo * ptr;
-
- ptr = (Echo *)Instance;
- switch (Port) {
- case DELAYTIME_L:
- ptr->delaytime_L = DataLocation;
- break;
- case DELAYTIME_R:
- ptr->delaytime_R = DataLocation;
- break;
- case FEEDBACK_L:
- ptr->feedback_L = DataLocation;
- break;
- case FEEDBACK_R:
- ptr->feedback_R = DataLocation;
- break;
- case STRENGTH_L:
- ptr->strength_L = DataLocation;
- break;
- case STRENGTH_R:
- ptr->strength_R = DataLocation;
- break;
- case MODE:
- ptr->mode = DataLocation;
- break;
- case HAAS:
- ptr->haas = DataLocation;
- break;
- case REV_OUTCH:
- ptr->rev_outch = DataLocation;
- break;
- case DRYLEVEL:
- ptr->drylevel = DataLocation;
- break;
- case INPUT_L:
- ptr->input_L = DataLocation;
- break;
- case OUTPUT_L:
- ptr->output_L = DataLocation;
- break;
- case INPUT_R:
- ptr->input_R = DataLocation;
- break;
- case OUTPUT_R:
- ptr->output_R = DataLocation;
- break;
- }
- }
- #define EPS 0.00000001f
- static inline float
- M(float x) {
- if ((x > EPS) || (x < -EPS))
- return x;
- else
- return 0.0f;
- }
- void
- run_Echo(LADSPA_Handle Instance,
- unsigned long SampleCount) {
-
- Echo * ptr;
- unsigned long sample_index;
- LADSPA_Data delaytime_L;
- LADSPA_Data delaytime_R;
- LADSPA_Data feedback_L;
- LADSPA_Data feedback_R;
- LADSPA_Data strength_L;
- LADSPA_Data strength_R;
- LADSPA_Data drylevel;
- LADSPA_Data mode;
- LADSPA_Data haas;
- LADSPA_Data rev_outch;
- LADSPA_Data * input_L;
- LADSPA_Data * output_L;
- LADSPA_Data * input_R;
- LADSPA_Data * output_R;
- unsigned long sample_rate;
- unsigned long buflen_L;
- unsigned long buflen_R;
- LADSPA_Data out_L = 0;
- LADSPA_Data out_R = 0;
- LADSPA_Data in_L = 0;
- LADSPA_Data in_R = 0;
- ptr = (Echo *)Instance;
- delaytime_L = LIMIT(*(ptr->delaytime_L),0.0f,2000.0f);
- delaytime_R = LIMIT(*(ptr->delaytime_R),0.0f,2000.0f);
- feedback_L = LIMIT(*(ptr->feedback_L) / 100.0, 0.0f, 100.0f);
- feedback_R = LIMIT(*(ptr->feedback_R) / 100.0, 0.0f, 100.0f);
- strength_L = db2lin(LIMIT(*(ptr->strength_L),-70.0f,10.0f));
- strength_R = db2lin(LIMIT(*(ptr->strength_R),-70.0f,10.0f));
- drylevel = db2lin(LIMIT(*(ptr->drylevel),-70.0f,10.0f));
- mode = LIMIT(*(ptr->mode),-2.0f,2.0f);
- haas = LIMIT(*(ptr->haas),-2.0f,2.0f);
- rev_outch = LIMIT(*(ptr->rev_outch),-2.0f,2.0f);
- input_L = ptr->input_L;
- output_L = ptr->output_L;
- input_R = ptr->input_R;
- output_R = ptr->output_R;
- sample_rate = ptr->sample_rate;
- buflen_L = delaytime_L * sample_rate / 1000;
- buflen_R = delaytime_R * sample_rate / 1000;
- for (sample_index = 0; sample_index < SampleCount; sample_index++) {
-
- in_L = *(input_L++);
- in_R = *(input_R++);
- out_L = in_L * drylevel + ptr->mpx_out_L * strength_L;
- out_R = in_R * drylevel + ptr->mpx_out_R * strength_R;
- if (haas > 0.0f)
- in_R = 0.0f;
- if (mode <= 0.0f) {
- ptr->mpx_out_L =
- M(push_buffer(in_L + ptr->mpx_out_L * feedback_L,
- ptr->ringbuffer_L, buflen_L, ptr->buffer_pos_L));
- ptr->mpx_out_R =
- M(push_buffer(in_R + ptr->mpx_out_R * feedback_R,
- ptr->ringbuffer_R, buflen_R, ptr->buffer_pos_R));
- } else {
- ptr->mpx_out_R =
- M(push_buffer(in_L + ptr->mpx_out_L * feedback_L,
- ptr->ringbuffer_L, buflen_L, ptr->buffer_pos_L));
- ptr->mpx_out_L =
- M(push_buffer(in_R + ptr->mpx_out_R * feedback_R,
- ptr->ringbuffer_R, buflen_R, ptr->buffer_pos_R));
- }
- if (rev_outch <= 0.0f) {
- *(output_L++) = out_L;
- *(output_R++) = out_R;
- } else {
- *(output_L++) = out_R;
- *(output_R++) = out_L;
- }
- }
- }
- void
- set_run_adding_gain(LADSPA_Handle Instance, LADSPA_Data gain){
- Echo * ptr;
- ptr = (Echo *)Instance;
- ptr->run_adding_gain = gain;
- }
- void
- run_adding_gain_Echo(LADSPA_Handle Instance,
- unsigned long SampleCount) {
-
- Echo * ptr;
- unsigned long sample_index;
- LADSPA_Data delaytime_L;
- LADSPA_Data delaytime_R;
- LADSPA_Data feedback_L;
- LADSPA_Data feedback_R;
- LADSPA_Data strength_L;
- LADSPA_Data strength_R;
- LADSPA_Data drylevel;
- LADSPA_Data mode;
- LADSPA_Data haas;
- LADSPA_Data rev_outch;
- LADSPA_Data * input_L;
- LADSPA_Data * output_L;
- LADSPA_Data * input_R;
- LADSPA_Data * output_R;
- unsigned long sample_rate;
- unsigned long buflen_L;
- unsigned long buflen_R;
- LADSPA_Data out_L = 0;
- LADSPA_Data out_R = 0;
- LADSPA_Data in_L = 0;
- LADSPA_Data in_R = 0;
- ptr = (Echo *)Instance;
- delaytime_L = LIMIT(*(ptr->delaytime_L),0.0f,2000.0f);
- delaytime_R = LIMIT(*(ptr->delaytime_R),0.0f,2000.0f);
- feedback_L = LIMIT(*(ptr->feedback_L) / 100.0, 0.0f, 100.0f);
- feedback_R = LIMIT(*(ptr->feedback_R) / 100.0, 0.0f, 100.0f);
- strength_L = db2lin(LIMIT(*(ptr->strength_L),-70.0f,10.0f));
- strength_R = db2lin(LIMIT(*(ptr->strength_R),-70.0f,10.0f));
- drylevel = db2lin(LIMIT(*(ptr->drylevel),-70.0f,10.0f));
- mode = LIMIT(*(ptr->mode),-2.0f,2.0f);
- haas = LIMIT(*(ptr->haas),-2.0f,2.0f);
- rev_outch = LIMIT(*(ptr->rev_outch),-2.0f,2.0f);
- input_L = ptr->input_L;
- output_L = ptr->output_L;
- input_R = ptr->input_R;
- output_R = ptr->output_R;
- sample_rate = ptr->sample_rate;
- buflen_L = delaytime_L * sample_rate / 1000;
- buflen_R = delaytime_R * sample_rate / 1000;
- for (sample_index = 0; sample_index < SampleCount; sample_index++) {
-
- in_L = *(input_L++);
- in_R = *(input_R++);
- out_L = in_L * drylevel + ptr->mpx_out_L * strength_L;
- out_R = in_R * drylevel + ptr->mpx_out_R * strength_R;
- if (haas > 0.0f)
- in_R = 0.0f;
- if (mode <= 0.0f) {
- ptr->mpx_out_L =
- M(push_buffer(in_L + ptr->mpx_out_L * feedback_L,
- ptr->ringbuffer_L, buflen_L, ptr->buffer_pos_L));
- ptr->mpx_out_R =
- M(push_buffer(in_R + ptr->mpx_out_R * feedback_R,
- ptr->ringbuffer_R, buflen_R, ptr->buffer_pos_R));
- } else {
- ptr->mpx_out_R =
- M(push_buffer(in_L + ptr->mpx_out_L * feedback_L,
- ptr->ringbuffer_L, buflen_L, ptr->buffer_pos_L));
- ptr->mpx_out_L =
- M(push_buffer(in_R + ptr->mpx_out_R * feedback_R,
- ptr->ringbuffer_R, buflen_R, ptr->buffer_pos_R));
- }
- if (rev_outch <= 0.0f) {
- *(output_L++) += out_L * ptr->run_adding_gain;
- *(output_R++) += out_R * ptr->run_adding_gain;
- } else {
- *(output_L++) += out_R * ptr->run_adding_gain;
- *(output_R++) += out_L * ptr->run_adding_gain;
- }
- }
- }
- /* Throw away an Echo effect instance. */
- void
- cleanup_Echo(LADSPA_Handle Instance) {
- Echo * ptr = (Echo *)Instance;
- free(ptr->ringbuffer_L);
- free(ptr->ringbuffer_R);
- free(ptr->buffer_pos_L);
- free(ptr->buffer_pos_R);
- free(Instance);
- }
- LADSPA_Descriptor * stereo_descriptor = NULL;
- /* __attribute__((constructor)) tap_init() is called automatically when the plugin library is first
- loaded. */
- void
- __attribute__((constructor)) tap_init() {
-
- char ** port_names;
- LADSPA_PortDescriptor * port_descriptors;
- LADSPA_PortRangeHint * port_range_hints;
-
- if ((stereo_descriptor =
- (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor))) == NULL)
- exit(1);
-
- /* init the stereo Echo */
-
- stereo_descriptor->UniqueID = ID_STEREO;
- stereo_descriptor->Label = strdup("tap_stereo_echo");
- stereo_descriptor->Properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
- stereo_descriptor->Name = strdup("TAP Stereo Echo");
- stereo_descriptor->Maker = strdup("Tom Szilagyi");
- stereo_descriptor->Copyright = strdup("GPL");
- stereo_descriptor->PortCount = PORTCOUNT_STEREO;
- if ((port_descriptors =
- (LADSPA_PortDescriptor *)calloc(PORTCOUNT_STEREO, sizeof(LADSPA_PortDescriptor))) == NULL)
- exit(1);
- stereo_descriptor->PortDescriptors = (const LADSPA_PortDescriptor *)port_descriptors;
- port_descriptors[DELAYTIME_L] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
- port_descriptors[DELAYTIME_R] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
- port_descriptors[FEEDBACK_L] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
- port_descriptors[FEEDBACK_R] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
- port_descriptors[STRENGTH_L] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
- port_descriptors[STRENGTH_R] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
- port_descriptors[DRYLEVEL] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
- port_descriptors[MODE] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
- port_descriptors[HAAS] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
- port_descriptors[REV_OUTCH] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
- port_descriptors[INPUT_L] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
- port_descriptors[OUTPUT_L] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
- port_descriptors[INPUT_R] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
- port_descriptors[OUTPUT_R] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
- if ((port_names =
- (char **)calloc(PORTCOUNT_STEREO, sizeof(char *))) == NULL)
- exit(1);
- stereo_descriptor->PortNames = (const char **)port_names;
- port_names[DELAYTIME_L] = strdup("L Delay [ms]");
- port_names[DELAYTIME_R] = strdup("R/Haas Delay [ms]");
- port_names[FEEDBACK_L] = strdup("L Feedback [%]");
- port_names[FEEDBACK_R] = strdup("R/Haas Feedback [%]");
- port_names[STRENGTH_L] = strdup("L Echo Level [dB]");
- port_names[STRENGTH_R] = strdup("R Echo Level [dB]");
- port_names[DRYLEVEL] = strdup("Dry Level [dB]");
- port_names[MODE] = strdup("Cross Mode");
- port_names[HAAS] = strdup("Haas Effect");
- port_names[REV_OUTCH] = strdup("Swap Outputs");
- port_names[INPUT_L] = strdup("Input Left");
- port_names[OUTPUT_L] = strdup("Output Left");
- port_names[INPUT_R] = strdup("Input Right");
- port_names[OUTPUT_R] = strdup("Output Right");
- if ((port_range_hints =
- ((LADSPA_PortRangeHint *)calloc(PORTCOUNT_STEREO, sizeof(LADSPA_PortRangeHint)))) == NULL)
- exit(1);
- stereo_descriptor->PortRangeHints = (const LADSPA_PortRangeHint *)port_range_hints;
- port_range_hints[DELAYTIME_L].HintDescriptor =
- (LADSPA_HINT_BOUNDED_BELOW |
- LADSPA_HINT_BOUNDED_ABOVE |
- LADSPA_HINT_DEFAULT_100);
- port_range_hints[DELAYTIME_L].LowerBound = 0;
- port_range_hints[DELAYTIME_L].UpperBound = MAX_DELAY;
- port_range_hints[DELAYTIME_R].HintDescriptor =
- (LADSPA_HINT_BOUNDED_BELOW |
- LADSPA_HINT_BOUNDED_ABOVE |
- LADSPA_HINT_DEFAULT_100);
- port_range_hints[DELAYTIME_R].LowerBound = 0;
- port_range_hints[DELAYTIME_R].UpperBound = MAX_DELAY;
- port_range_hints[FEEDBACK_L].HintDescriptor =
- (LADSPA_HINT_BOUNDED_BELOW |
- LADSPA_HINT_BOUNDED_ABOVE |
- LADSPA_HINT_DEFAULT_0);
- port_range_hints[FEEDBACK_L].LowerBound = 0;
- port_range_hints[FEEDBACK_L].UpperBound = 100;
- port_range_hints[FEEDBACK_R].HintDescriptor =
- (LADSPA_HINT_BOUNDED_BELOW |
- LADSPA_HINT_BOUNDED_ABOVE |
- LADSPA_HINT_DEFAULT_0);
- port_range_hints[FEEDBACK_R].LowerBound = 0;
- port_range_hints[FEEDBACK_R].UpperBound = 100;
- port_range_hints[STRENGTH_L].HintDescriptor =
- (LADSPA_HINT_BOUNDED_BELOW |
- LADSPA_HINT_BOUNDED_ABOVE |
- LADSPA_HINT_DEFAULT_0);
- port_range_hints[STRENGTH_L].LowerBound = -70;
- port_range_hints[STRENGTH_L].UpperBound = 10;
- port_range_hints[STRENGTH_R].HintDescriptor =
- (LADSPA_HINT_BOUNDED_BELOW |
- LADSPA_HINT_BOUNDED_ABOVE |
- LADSPA_HINT_DEFAULT_0);
- port_range_hints[STRENGTH_R].LowerBound = -70;
- port_range_hints[STRENGTH_R].UpperBound = 10;
- port_range_hints[MODE].HintDescriptor =
- (LADSPA_HINT_TOGGLED |
- LADSPA_HINT_DEFAULT_0);
- port_range_hints[HAAS].HintDescriptor =
- (LADSPA_HINT_TOGGLED |
- LADSPA_HINT_DEFAULT_0);
- port_range_hints[REV_OUTCH].HintDescriptor =
- (LADSPA_HINT_TOGGLED |
- LADSPA_HINT_DEFAULT_0);
- port_range_hints[DRYLEVEL].HintDescriptor =
- (LADSPA_HINT_BOUNDED_BELOW |
- LADSPA_HINT_BOUNDED_ABOVE |
- LADSPA_HINT_DEFAULT_0);
- port_range_hints[DRYLEVEL].LowerBound = -70;
- port_range_hints[DRYLEVEL].UpperBound = 10;
- port_range_hints[INPUT_L].HintDescriptor = 0;
- port_range_hints[OUTPUT_L].HintDescriptor = 0;
- port_range_hints[INPUT_R].HintDescriptor = 0;
- port_range_hints[OUTPUT_R].HintDescriptor = 0;
- stereo_descriptor->instantiate = instantiate_Echo;
- stereo_descriptor->connect_port = connect_port_Echo;
- stereo_descriptor->activate = activate_Echo;
- stereo_descriptor->run = run_Echo;
- stereo_descriptor->run_adding = run_adding_gain_Echo;
- stereo_descriptor->set_run_adding_gain = set_run_adding_gain;
- stereo_descriptor->deactivate = NULL;
- stereo_descriptor->cleanup = cleanup_Echo;
-
- }
- void
- delete_descriptor(LADSPA_Descriptor * descriptor) {
- unsigned long index;
- if (descriptor) {
- free((char *)descriptor->Label);
- free((char *)descriptor->Name);
- free((char *)descriptor->Maker);
- free((char *)descriptor->Copyright);
- free((LADSPA_PortDescriptor *)descriptor->PortDescriptors);
- for (index = 0; index < descriptor->PortCount; index++)
- free((char *)(descriptor->PortNames[index]));
- free((char **)descriptor->PortNames);
- free((LADSPA_PortRangeHint *)descriptor->PortRangeHints);
- free(descriptor);
- }
- }
- /* __attribute__((destructor)) tap_fini() is called automatically when the library is unloaded. */
- void
- __attribute__((destructor)) tap_fini() {
- delete_descriptor(stereo_descriptor);
- }
- /* Return a descriptor of the requested plugin type. */
- const
- LADSPA_Descriptor *
- ladspa_descriptor(unsigned long Index) {
- switch (Index) {
- case 0:
- return stereo_descriptor;
- default:
- return NULL;
- }
- }
|