package LinuxIsdn::Call;
use strict;
use warnings;
use Carp;
use WeakRef;

use base   qw(Exporter);
use fields qw(debug controller on_hangup on_rung on_pickup
              audio_mode silence_sensitivity silence_interval
              dtmf_sensitivity dtmf_duration generation
              recorder transmitter);
use fields::accessor qw(debug		$
                        on_hangup	$
                        on_rung		$
                        on_pickup	$
                        recorder	$
                        transmitter	$
                        generation	.
                        );

# Must come after the class definition that can be
# used by recorder and transmitter
use LinuxIsdn::Call::Recorder;
use LinuxIsdn::Call::Transmitter;

our $VERSION   = "0.2";
our @EXPORT    = qw();
our @EXPORT_OK = qw();

sub nop {
}

sub new {
    my $class = shift;
    my LinuxIsdn::Call $call = $class->anew();
    $call->{on_rung} = $call->{on_pickup} = $call->{on_hangup} = \&nop;
    $call->{audio_mode} = $audio_mode{"ADPCM-2"};
    $call->{silence_sensitivity} = 0;
    $call->{silence_interval} = 70;	# 7 sec
    $call->{dtmf_sensitivity} = 0;
    $call->{dtmf_duration}    = 8;	# 40 ms
    weaken($call->{controller} = shift);
    $call->{outgoing}  = shift || 0;
    $call->{connected} = shift || 0;
    $call->{debug}     = shift || 0;
    return $call;
}

sub active {
    my LinuxIsdn::Call $call = shift;
    return $call->{controller};
}

sub silence_sensitivity {
    my LinuxIsdn::Call $call = shift;
    if ($_[0]) {
        my $nr = shift;
        croak "Silence sensitivity must be in [0,1], not $nr" if
            $nr < 0 || $nr > 1;
        $nr = int($nr * 32);
        $nr = 31 if $nr == 32;
        $call->{silence_sensitivity} = $nr;
    }
    return $call->{silence_sensitivity}/31;
}

sub silence_interval {
    my LinuxIsdn::Call $call = shift;
    if ($_[0]) {
        my $nr = int($_[0] * 10+0.5);
        croak "Silence detection must be in [0, 25.55[, not $_[0]" if
            $nr < 0 || $nr > 255;
        $call->{silence_interval} = $nr;
    }
    return $call->{silence_interval}/10;
}

sub dtmf_sensitivity {
    my LinuxIsdn::Call $call = shift;
    if ($_[0]) {
        my $nr = shift;
        croak "DTMF sensitivity must be in [0,1], not $nr" if
            $nr < 0 || $nr > 1;
        $nr = int($nr * 16);
        $nr = 15 if $nr == 16;
        $call->{dtmf_sensitivity} = $nr;
    }
    return $call->{dtmf_sensitivity}/15;
}

sub dtmf_duration {
    my LinuxIsdn::Call $call = shift;
    if ($_[0]) {
        my $nr = int($_[0] * 200+0.5);
        croak "Silence detection must be in [0, 1.2775[, not $_[0]" if
            $nr < 0 || $nr > 255;
        $call->{dtmf_duration} = $nr;
    }
    return $call->{dtmf_duration} * 0.005;
}

sub audio_mode {
    my LinuxIsdn::Call $call = shift;
    if ($_[0]) {
        my $nr = lc($_[0]);
        $nr =~ s/-//g;
        defined($nr = $audio_mode{$nr}) || croak "Unknown audio_mode $_[0]";
        $call->{audio_mode} = $nr;
    }
    return $audio_mode[$call->{audio_mode}];
}

sub detach {
    my LinuxIsdn::Call $call = shift;
    my LinuxIsdn       $self = $call->{controller};
    if ($self) {
        $self->{call} = undef;
        $call->{controller} = undef;
    }
    return $call;
}

sub do_rung {
    my LinuxIsdn::Call $call = shift;
    $call->{connected} = 0;
    $call->detach();
    $call->{on_rung}->($call, @_);
}

sub do_pickup {
    my LinuxIsdn::Call $call = shift;
    $call->{connected} = 1;
    $call->{generation}++;
    $call->{on_pickup}->($call, @_);
}

sub do_hangup {
    my LinuxIsdn::Call $call = shift;
    $call->{connected} = 0;
    $call->detach();
    $call->{on_hangup}->($call, @_);
}

sub do_end_recording {
    my LinuxIsdn::Call $call = shift;
    my LinuxIsdn::Call::Recorder $record = $call->{recorder};
    return unless $record;

    $call->{recorder} = undef;
    $record->{call} = undef;
    $record->end($call, @_);
    $record->deactivate($call);
}

sub do_end_transmitting {
    my LinuxIsdn::Call $call = shift;
    my LinuxIsdn::Call::Transmitter $transmit = $call->{transmitter};
    return unless $transmit;

    $call->{transmitter} = undef;
    $transmit->{call} = undef;
    $transmit->end($call, @_);
    $transmit->deactivate($call);
}

sub pickup {
    my LinuxIsdn::Call $call = shift;
    my LinuxIsdn       $self = $call->{controller};
    return unless $self;
    $self->ata($call);
    $call->{on_pickup} = shift || $call->{on_pickup};
    $call->{on_rung}   = shift || $call->{on_rung};
    $call->{on_hangup} = shift || $call->{on_hangup};
    return $call;
}

sub record {
    my LinuxIsdn::Call $call = shift;
    my LinuxIsdn::Call::Recorder $record = shift ||
        croak "$call->record() needs a LinuxIsdn::Recorder argument";
    my LinuxIsdn $self = $call->{controller};
    return unless $self && $call->{connected};
    $record->on_start($_[0]) if $_[0];
    $record->activate($call);
    my $generation = $call->{generation};
    $self->vrx($call, sub { 
        if ($call->{generation} != $generation) {
            $record->deactivate($call, "Call gone befored recording started");
            return;
        }
        $call->{recorder}      = $record;
        weaken($record->{call} = $call);
        $record->start();
    });
    return $call;
}

sub transmit {
    my LinuxIsdn::Call $call = shift;
    my LinuxIsdn::Call::Transmitter $transmit = shift ||
        croak "$call->transmit() needs a LinuxIsdn::Transmitter argument";
    my LinuxIsdn $self = $call->{controller};
    return unless $self && $call->{connected};
    $transmit->on_start($_[0]) if $_[0];
    $transmit->activate($call);
    my $generation = $call->{generation};
    $self->vtx($call, sub { 
        if ($call->{generation} != $generation) {
            $transmit->deactivate($call, 
                                  "Call gone befored transmitting started");
            return;
        }
        $call->{transmitter} = $transmit;
        weaken($transmit->{call} = $call);
        $transmit->start();
    });
    return $call;
}

sub record_transmit {
    my LinuxIsdn::Call $call = shift;
    my LinuxIsdn::Call::Recorder $record = shift ||
        croak "$call->record() needs a LinuxIsdn::Recorder argument";
    my LinuxIsdn::Call::Recorder $transmit = shift ||
        croak "$call->transmit() needs a LinuxIsdn::Transmitter argument";
    my LinuxIsdn $self = $call->{controller};
    return unless $self && $call->{connected};
    $record->on_start($_[0])   if $_[0];
    $transmit->on_start($_[1]) if $_[1];
    $record->activate($call);
    $transmit->activate($call);
    my $generation = $call->{generation};
    $self->vrtx($call, sub { 
        if ($call->{generation} != $generation) {
            $record->deactivate($call, "Call gone befored combined recording/transmitting started");
            $transmit->deactivate($call, "Call gone befored combined recording/transmitting started");
            return;
        }

        $call->{recorder}      = $record;
        weaken($record->{call} = $call);
        $call->{transmitter} = $transmit;
        weaken($transmit->{call} = $call);

        $record->start();
        $transmit->start();
    });
    return $call;
}

sub ignore {
    my LinuxIsdn::Call $call = shift;
    my LinuxIsdn       $self = $call->{controller};
    return unless $self;
    $call->{on_rung}   = shift || $call->{on_rung};
    local $LinuxIsdn::early = 1;
    $self->command("ATH");
    return $call;
}

sub _abort_recording {
    my LinuxIsdn::Call $call = shift;
    my LinuxIsdn       $self = $call->{controller};
    return unless $self;
    $self->_abort_recording(@_);
    return $call;
}

sub _abort_transmitting {
    my LinuxIsdn::Call $call = shift;
    my LinuxIsdn $self = $call->{controller};
    return unless $self;
    $self->_abort_transmitting(@_);
    return $call;
}

sub hangup {
    my LinuxIsdn::Call $call = shift;
    my LinuxIsdn       $isdn = $call->{controller};
    return unless $isdn;
    $call->{on_hangup} = shift || $call->{on_hangup};
    $call->{transmitter}->abort($_[1]) if $call->{transmitter};
    $call->{recorder}->abort($_[1])    if $call->{recorder};
    $isdn->command("ATH", $_[0]);
    return $call;
}

#sub DESTROY {
#    my LinuxIsdn::Call $self = shift;
#     print STDERR "morituri te salutant: $self\n";
#}

1;

__END__

=head1 DESCRIPTION

Helper library for L<LinuxIsdn|LinuxIsdn>. Managing calls.

=head1 SYNOPSIS

 use LinuxIsdn::Call;

 $call        = LinuxIsdn::Call->new(?$controller,
                                     ?$outgoing, ?$connected, ?$debug????);
 $outgoing    = $call->outgoing(?$new_outgoing?);
 $connected   = $call->connected(?$new_connected?);
 $debug       = $call->debug(?$new_debug?);
 $isdn        = $call->active();
 $old_fun     = $call->on_rung(?$new_fun?);
 $old_fun     = $call->on_pickup(?$new_fun?);
 $self        = $call->pickup(?$pickup_fun, ?rung_fun, ?$hangup_fun???);
 $self        = $call->ignore(?rung_fun?);
 $old_fun     = $call->on_hangup(?$new_fun?);
 $self        = $call->hangup(?$hangup_fun?);

 $str	      = $call->string();
 $octet3      = $call->octet3(?$new_plan?);
 @LinuxIsdn::Call::number_type;
 @types       = LinuxIsdn::Call::number_types();
 $type        = $call->number_type(?$numeric?);
 @LinuxIsdn::Call::number_plan;
 @plans       = LinuxIsdn::Call::number_plans();
 $plan        = $call->number_plan(?$numeric?);
 $octet3a     = $call->octet3a(?$new_info?);
 @LinuxIsdn::Call::presentation_indicator;
 @indicator   = LinuxIsdn::Call::presentation_indicators();
 $indicator   = $call->presentation_indicator(?$numeric?);
 @LinuxIsdn::Call::screening_indicator;
 @screening   = LinuxIsdn::Call::screening_indicators();
 $screening   = $call->screening_indicator(?$numeric?);
 $service     = $call->service(?$new_service?);
 $rings       = $call->rings(?$new_rings?);
 $remote_number = $call->remote_number(?$new_number?);
 $local_number= $call->local_number(?$new_number?);

 $mode        = $call->audio_mode(?$new_mode?);
 $sensitivity = $call->silence_sensitivity(?$new_sensitivity?);
 $interval    = $call->silence_interval(?$new_interval?);
 $sensitivity = $call->dtmf_sensitivity(?$new_sensitivity?);
 $duration    = $call->dtmf_duration(?$new_duration?);

 $self        = $call->record();
 $recorder    = $call->recorder(?$new_recorder?);

 $self        = $call->transmit();
 $transmitter = $call->transmitter(?$new_transmitter?);

 LinuxIsdn::Call::nop();

=head1 METHODS

Notice that all methods that cause a tty command to be executed, do this
by queing the command, which will later be processed in the event loop.
That means that if you e.g. give a L<hangup|"hangup"> command,
your following statement will not be executed with the phone hung up.

=over 4

=item $call = LinuxIsdn::Call->X<new>(?$controller, ?$outgoing,
                                      ?$connected, ?$debug????)

Create a call object. Possibly sets the $controller object (the causing
L<LinuxIsdn|LinuxIsdn> object), $outgoing (object describes an call initiated
by us, not us being called), $connected (wethere the call is connected) and
debug mode.
All optional arguments default to 0, except $controller which defaults to
undef.

This is normally not called by the user, but internally by
L<LinuxIsdn|LinuxIsdn> and passed to the user through the the
L<on_ring|LinuxIsdn/"on_ring"> callback (see L<LinuxIsdn>).

=item $outgoing = $call->X<outgoing>(?$new_outgoing?)

If $new_outgoing is given, sets the internal variable to that value
(normally not set by the user, but implicitely by L<LinuxIsdn|LinuxIsdn>).
Then returns the value of the internal variable that tells if this object
describes an outgoing telephone call. This value makes sense both before and
after the connection is made, and is remembered after hangup.

Preferred values are:

=over 8

=item undef

Call object does not know the direction of the call. Cannot happen with the
standard L<LinuxIsdn|LinuxIsdn> class.

=item 0

This object describes an incoming call.

=item 1

This object describes an outgoing call.

=back

=item $connected = $call->X<connected>(?$new_connected?)

If $new_connected is given, sets the internal variable to that value
(normally not set by the user, but implicitely by L<LinuxIsdn|LinuxIsdn>).
Then returns the value of the internal variable that tells if this object
escribes a connected telephone call. The value will be 0 while calling/a call
attempt is incoming (see L<outgoing|"outgoing">, will be 1 after pickup and
0 again after hangup.

Preferred values are

=over 8

=item undef

Call object does not know if the call is connected. Cannot happen with the
standard L<LinuxIsdn|LinuxIsdn> class.

=item 0

This object describes an unconnected call (before pickup or after hangup).
You can use L<active|"active"> to recognize if hangup/rung has happened.

=item 1

This object describes a connected call. Guaranteed from pickup to hangup.

=back

=item $debug = $call->X<debug>(?$new_debug?)

Optionally sets the current debug state to $new_debug. The returns the
current debug state.

The bits are:

=over 8

=item any

Turn on general debugging.

=item 2

Turn on command/state machine machine debugging.

=item 4

Turn on audio chunk debugging.

=back

=item $isdn = $call->X<active>()

Returns the L<LinuxIsdn::LinuxIsdn> object $isdn this call belongs to. If
the call is gone (meaning $call is a dead object), will return undef.

=item $old_fun = $call->X<on_rung>(?$new_fun?)

Returns the function reference that will be executed when a call disappears
without having been picked up (RUNG message). If given an argument, will
replace the current function. The function will be called as
S<$new_fun-E<gt>($call, $controller)>, where the L<LinuxIsdn|"LinuxIsdn">
object $controller describes the device that received the .
The default value on object creation is L<nop|"nop">.

During this call, L<connected|"connected"> will already be false, and
L<active|"active"> has been lost (but you got passed the $controller as
one of the parameters. Notice that this is your last chance to save
$controller if it interests you. LinuxIsdn will forget about it from this
point on).

Usually set with L<pickup|"pickup">.

=item $old_fun = $call->X<on_pickup>(?$new_fun?)

Returns the function reference that will be executed when a connection
is made (VCON message). If given an argument, will replace the current
function. The callback is called as S<$new_fun-E<gt>($call)>. The default
value on object creation is L<nop|"nop">. You can be sure that
$call->L<active|"active">() is valid during the callback and that
L<connected|"connected"> has already been set to 1.

Usually set with L<pickup|"pickup">.

=item $self = $call->X<pickup>(?$pickup_fun, ?$rung_fun, ?$hangup_fun???)

Gives a command to pick up the phone and optionally sets what should be done
on the two possible results of a pickup (success, see L<on_pickup|"on_pickup">
and call gone before the pickup is done, see L<on_rung|"on_rung">).
Also determines what to do a successful call is finished (see
L<on_hangup|"on_hangup">)

The typical moment to call this method is during the
L<LinuxIsdn::on_ring|LinuxIsdn/"on_ring"> callback.

returns $call. Internally uses L<on_pickup|"on_pickup">,
L<on_rung|"on_rung"> and L<on_hangup|"on_hangup">

=item  $self = $call->X<ignore>(?$rung_fun?)

Tells the system you're not interested in the current caller. If $rung_fun
is given, that will become the new L<on_rung|"on_rung"> callback. When the
ignore command comes through, the on_rung callback will be called and the
system will again consider other incoming calls.
(Don't confuse this with reject. Other devices on the bus may still pick
up this call).

=item $old_fun = $call->X<on_hangup>(?$new_fun?)

Returns the function reference that will be executed when a connection
is broken (NO CARRIER message). If given an argument, will replace the
current function. The callback will be called as 
S<$fun-E<gt>($call, $controller, $reason)>, where the L<LinuxIsdn|"LinuxIsdn">
object $controller describes the device that received the hangup and $resaon
is the reason string returned in the B<NO CARRIER> message.
The default value on object creation is L<nop|"nop">. This callback can get
called if and only if the L<on_pickup|"on_pickup"> was called (you can
only hangup lines that were picked up in the first place).

During this call, L<connected|"connected"> will already be false, and
L<active|"active"> has been lost (but you got passed the $controller as
one of the parameters. Notice that this is your last chance to save
$controller if it interests you. LinuxIsdn will forget about it from this
point on).

Usually set with L<pickup|"pickup"> or L<hangup|"hangup">

=item $self = $call->X<hangup>(?$fun?)

Gives a command to hang up the phone. if $fun is given, that will become the
new L<on_hangup|"on_hangup"> callback. returns $call if a hangup was queued
(A call is in progress, picked up or not), or undef otherwise ($call is dead).

=item $str = $call->X<string>()

Returns a string describing this call object.

Example output (not all these fields will necessarily be output):

    Remote number  686415890
    Local  number  548913604
    Number plan    33
    Screening info 131
    Service type   1
    Rings          4

=item $octet3    = $call->X<octet3>(?$new_plan?)

Returns the numbering plan (octet 3 of calling party number IE, see
section 4.5.10 of ITU Q.931). Can also be used to change the value
(the value is set up at call creation and not used anymore internally).
Use L<number_type|"number_type"> and L<number_plan|"number_plan"> to
parse this field. Will be filled in from just before the moment the
L<on_ring|"on_ring"> callback is activated.

=item @LinuxIsdn::Call::number_type

An array of number types strings from section 4.5.10 of ITU Q.931.

=item @types   = LinuxIsdn::Call::Xnumber_types();

Returns L<@LinuxIsdn::Call::number_type|"@LinuxIsdn::Call::number_type">.

=item $type    = $call->X<number_type>(?$numeric?)

Returns the number type of this call if $numeric is given and true, or
the corresponding entry in
L<@LinuxIsdn::Call::number_type|"@LinuxIsdn::Call::number_type">
otherwise. Will be filled in from just before the moment the
L<on_ring|"on_ring"> callback is activated.

=item @LinuxIsdn::Call::number_plan

An array of number plans strings from section 4.5.10 of ITU Q.931.

=item @plans   = LinuxIsdn::Call::number_plans()

Returns L<@LinuxIsdn::Call::number_plan|"@LinuxIsdn::Call::number_plan">.

=item $plan    = $call->X<number_plan>(?$numeric?)

Returns the number plan of this call if $numeric is given and true, or
the corresponding entry in
L<@LinuxIsdn::Call::number_plan|"@LinuxIsdn::Call::number_plan">
otherwise. Will be filled in from just before the moment the
L<on_ring|"on_ring"> callback is activated.

=item $octet3a = $call->X<octet3a>(?$new_info?);

Returns the screening info (octet 3a of calling party number IE , see
section 4.5.10 of ITU Q.931).  Can also be used to change the value
(the value is set up at call creation and not used anymore internally).
Use L<presentation_indicator|"presentation_indicator"> and
L<screening_indicator|"screening_indicator"> to parse this field. Will be
filled in from just before the moment the L<on_ring|"on_ring"> callback is
activated.

=item @LinuxIsdn::Call::presentation_indicator

An array of presentation indicator strings from section 4.5.10 of ITU Q.931.

=item @indicator = LinuxIsdn::Call::presentation_indicators()

Returns L<@LinuxIsdn::Call::presentation_indicator|"@LinuxIsdn::Call::presentation_indicator">.

=item $indicator = $call->X<presentation_indicator>(?$numeric?)

Returns the presentation indicator of this call if $numeric is given and true,
or the corresponding entry in
L<@LinuxIsdn::Call::presentation_indicator|"@LinuxIsdn::Call::presentation_indicator">
otherwise. Will be filled in from just before the moment the
L<on_ring|"on_ring"> callback is activated.

=item @LinuxIsdn::Call::screening_indicator

An array of screening indicator strings from section 4.5.10 of ITU Q.931.

=item @screening = LinuxIsdn::Call::screening_indicators()

Returns L<@LinuxIsdn::Call::screening_indicator|"@LinuxIsdn::Call::screening_indicator">.

=item $screening = $call->X<screening_indicator>(?$numeric?)

Returns the screening indicator of this call if $numeric is given and true, or
the corresponding entry in
L<@LinuxIsdn::Call::screening_indicator|"@LinuxIsdn::Call::screening_indicator">
otherwise. Will be filled in from just before the moment the
L<on_ring|"on_ring"> callback is activated.

=item $service = $call->X<service>(?$new_service?);

Returns the service type (Service-Octet-1). Coded as:

    Bit 0:    Service 1 (audio) when set.
    Bit 1:    Service 5 (BTX)   when set.
    Bit 2:    Service 7 (data)  when set.

Can also be used to change the value
(the value is set up at call creation and not used anymore internally).

=item $rings = $call->X<rings>(?$new_rings?)

Returns the number of rings at the moment of pickup (so don't expect
anything usefull here before that point).
Can also be used to change the value
(the value is set up at pickup time and not used anymore internally).

=item $remote_number = $call->X<remote_number>(?$new_number?);

Returns the remote telephone number (including zone)
Can also be used to change the value
(the value is set up at call creation and not used anymore internally).

=item $local_number = $call->X<local_number>(?$new_number?)

Returns the local telephone number (including zone).
(Yes, this IS usefull if you have multiple subscriber numbers).
Can also be used to change the value
(the value is set up at call creation and not used anymore internally).

=item $mode = $call->X<audio_mode>(?$new_mode?)

If given and defined, sets the audio_mode implied in following
L<record|"record"> or L<transmit|"transmit"> calls (has no influence on
anything currently running or scheduled). Returns the current audio mode, which
will be one of:

=over 8

=item ADPCM-2

8000 samples/sec, 2 bit/sample The default.

=item ADPCM-3

8000 samples/sec, 3 bit/sample

=item ADPCM-4

8000 samples/sec, 4 bit/sample

=item aLAW

8000 samples/sec, 8 bit/sample

=item uLAW

8000 samples/sec. 8 bit/sample. This is what F</dev/audio> on most unix
systems speaks.

=back

=item $sensitivity = $call->X<silence_sensitivity>(?$new_sensitivity?)

Sets the sensitivity for silence detection (See
L<LinuxIsdn::Call::Recorder::silence|LinuxIsdn::Call::Recorder/"silence"> and
L<LinuxIsdn::Call::Recorder::quiet|LinuxIsdn::Call::Recorder/"quiet">) to
$new_sensitivity if given and defined.
Then returns the current sensitivity. Sensitivity will be a number in the
interval [0, 1], and will be quantised to the available resolution.
Any sensitivity that's small enough to end up as 0 deactivates silence
detection. Defaults to 0 on call object creation.

=item $interval = $call->X<silence_interval>(?$new_interval?)

Sets the interval over which the sound must be below the threshold before
it's called "silence" (See
L<LinuxIsdn::Call::Recorder::silence|LinuxIsdn::Call::Recorder/"silence"> and
L<LinuxIsdn::Call::Recorder::quiet|LinuxIsdn::Call::Recorder/"quiet">) to
$new_interval if given and defined. Then returns the current interval.

The interval must be given in seconds (can be fractional !) and is quantised.
Currently can run from [0, 25.5] in units of 0.1 sec.

=item $sensitivity = $call->X<dtmf_sensitivity>(?$new_sensitivity?)

Sets the sensitivity for DTMF detection (see
L<LinuxIsdn::Call::Recorder::dtmf|LinuxIsdn::Call::Recorder/"dtmf">) to
$new_sensitivity if given and defined.
Then returns the current sensitivity. Sensitivity will be a number in the
interval [0..1], and will be quantised to the available resolution.
Any sensitivity that's small enough to end up as 0 means soft decode, while
all others mean hardware decode. Defaults to 0 on call object creation.

=item $duration = $call->X<dtmf_duration>(?$new_duration?)

Sets the duration needed before a tone is detected as DTMF (see 
L<LinuxIsdn::Call::Recorder::dtmf|LinuxIsdn::Call::Recorder/"dtmf">) to
$new_duration  if given and defined. The returns the current duration.
Duration is in seconds (can be fractional !) and will be quantised to the
available resolution.
Currently can run from [0, 1.275] in units of 0.005 sec.

=item $self = $call->X<record>()

Queue a audio record command (but only if this call is active). When recording
starts, the 
L<LinuxIsdn::Call::Recorder::end|LinuxIsdn::Call::Recorder/"end"> method will
be called. This may never happen if the line is hung up before the queued
start recording command is executed by the ISDN device (in which case 
L<LinuxIsdn::Call::Recorder::activate|LinuxIsdn::Call::Recorder/"activate"> is
called with an appropiate error message. Returns $call if we currently
are connected, otherwise does nothing and returns undef.

=item $self = $call->X<transmit>()

Queue a audio transmit command. When transmitting starts, the 
L<on_transmitting|"on_transmitting"> callback will be called. This may never
happen if the line is hung up before this point. Returns $call if we currently
are connected, otherwise does nothing and returns undef.

=item $transmitter = $call->X<transmitter>(?$new_transmitter?)

Optionally set's the internal transmitter object to $new_transmitter if given,
and then returns the internal transmitter object. Undef normally means there's
no transmitting going on (is internally set to a value after the
L<on_transmitting|"on_transmitting"> callback finishes, and set to undef when
the audio transmitting ends.

=item LinuxIsdn::Call::X<nop>()

Does absolutely nothing. Typically used as a callback placeholder in case
you want nothing special to happen on callback.

=back

=head1 BUGS

None known

=head1 AUTHOR

Ton Hospel (LinuxIsdn-Call@ton.iguana.be)

=head1 SEE ALSO

L<LinuxIsdn::Call::Transmitter>,
L<LinuxIsdn::Call::Recorder>,
L<LinuxIsdn>,
L<IsdnDaemon>,
L<ITU Q.931>

=cut
