1 /*****************************************************************************
2 * alsa.c : alsa plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2000-2010 VLC authors and VideoLAN
5 * Copyright (C) 2009-2011 RĂ©mi Denis-Courmont
7 * Authors: Henri Fallon <henri@videolan.org> - Original Author
8 * Jeffrey Baker <jwbaker@acm.org> - Port to ALSA 1.0 API
9 * John Paul Lorenti <jpl31@columbia.edu> - Device selection
10 * Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr> - S/PDIF and aout3
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation; either version 2.1 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_dialog.h>
39 #include <alsa/asoundlib.h>
40 #include <alsa/version.h>
42 /** Private data for an ALSA PCM playback stream */
46 unsigned rate
; /**< Sample rate */
47 vlc_fourcc_t format
; /**< Sample format */
48 uint8_t chans_table
[AOUT_CHAN_MAX
]; /**< Channels order table */
49 uint8_t chans_to_reorder
; /**< Number of channels to reorder */
56 #include "audio_output/volume.h"
58 #define A52_FRAME_NB 1536
60 static int Open (vlc_object_t
*);
61 static void Close (vlc_object_t
*);
62 static int EnumDevices (vlc_object_t
*, char const *, char ***, char ***);
64 #define AUDIO_DEV_TEXT N_("Audio output device")
65 #define AUDIO_DEV_LONGTEXT N_("Audio output device (using ALSA syntax).")
67 #define AUDIO_CHAN_TEXT N_("Audio output channels")
68 #define AUDIO_CHAN_LONGTEXT N_("Channels available for audio output. " \
69 "If the input has more channels than the output, it will be down-mixed. " \
70 "This parameter is ignored when digital pass-through is active.")
71 static const int channels
[] = {
72 AOUT_CHAN_CENTER
, AOUT_CHANS_STEREO
, AOUT_CHANS_4_0
, AOUT_CHANS_4_1
,
73 AOUT_CHANS_5_0
, AOUT_CHANS_5_1
, AOUT_CHANS_7_1
,
75 static const char *const channels_text
[] = {
76 N_("Mono"), N_("Stereo"), N_("Surround 4.0"), N_("Surround 4.1"),
77 N_("Surround 5.0"), N_("Surround 5.1"), N_("Surround 7.1"),
81 set_shortname( "ALSA" )
82 set_description( N_("ALSA audio output") )
83 set_category( CAT_AUDIO
)
84 set_subcategory( SUBCAT_AUDIO_AOUT
)
85 add_string ("alsa-audio-device", "default",
86 AUDIO_DEV_TEXT
, AUDIO_DEV_LONGTEXT
, false)
87 change_string_cb (EnumDevices
)
88 add_integer ("alsa-audio-channels", AOUT_CHANS_FRONT
,
89 AUDIO_CHAN_TEXT
, AUDIO_CHAN_LONGTEXT
, false)
90 change_integer_list (channels
, channels_text
)
92 set_capability( "audio output", 150 )
93 set_callbacks( Open
, Close
)
97 /** Helper for ALSA -> VLC debugging output */
98 static void Dump (vlc_object_t
*obj
, const char *msg
,
99 int (*cb
)(void *, snd_output_t
*), void *p
)
101 snd_output_t
*output
;
104 if (unlikely(snd_output_buffer_open (&output
)))
107 int val
= cb (p
, output
);
110 msg_Warn (obj
, "cannot get info: %s", snd_strerror (val
));
114 size_t len
= snd_output_buffer_string (output
, &str
);
115 if (len
> 0 && str
[len
- 1])
116 len
--; /* strip trailing newline */
117 msg_Dbg (obj
, "%s%.*s", msg
, (int)len
, str
);
118 snd_output_close (output
);
120 #define Dump(o, m, cb, p) \
121 Dump(VLC_OBJECT(o), m, (int (*)(void *, snd_output_t *))(cb), p)
123 static void DumpDevice (vlc_object_t
*obj
, snd_pcm_t
*pcm
)
125 snd_pcm_info_t
*info
;
127 Dump (obj
, " ", snd_pcm_dump
, pcm
);
128 snd_pcm_info_alloca (&info
);
129 if (snd_pcm_info (pcm
, info
) == 0)
131 msg_Dbg (obj
, " device name : %s", snd_pcm_info_get_name (info
));
132 msg_Dbg (obj
, " device ID : %s", snd_pcm_info_get_id (info
));
133 msg_Dbg (obj
, " subdevice name: %s",
134 snd_pcm_info_get_subdevice_name (info
));
138 static void DumpDeviceStatus (vlc_object_t
*obj
, snd_pcm_t
*pcm
)
140 snd_pcm_status_t
*status
;
142 snd_pcm_status_alloca (&status
);
143 snd_pcm_status (pcm
, status
);
144 Dump (obj
, "current status:\n", snd_pcm_status_dump
, status
);
146 #define DumpDeviceStatus(o, p) DumpDeviceStatus(VLC_OBJECT(o), p)
148 #if (SND_LIB_VERSION >= 0x01001B)
149 static const uint16_t vlc_chans
[] = {
150 [SND_CHMAP_MONO
] = AOUT_CHAN_CENTER
,
151 [SND_CHMAP_FL
] = AOUT_CHAN_LEFT
,
152 [SND_CHMAP_FR
] = AOUT_CHAN_RIGHT
,
153 [SND_CHMAP_RL
] = AOUT_CHAN_REARLEFT
,
154 [SND_CHMAP_RR
] = AOUT_CHAN_REARRIGHT
,
155 [SND_CHMAP_FC
] = AOUT_CHAN_CENTER
,
156 [SND_CHMAP_LFE
] = AOUT_CHAN_LFE
,
157 [SND_CHMAP_SL
] = AOUT_CHAN_MIDDLELEFT
,
158 [SND_CHMAP_SR
] = AOUT_CHAN_MIDDLERIGHT
,
159 [SND_CHMAP_RC
] = AOUT_CHAN_REARCENTER
,
161 static_assert(AOUT_CHAN_MAX
== 9, "Missing channel entries");
163 static int Map2Mask (vlc_object_t
*obj
, const snd_pcm_chmap_t
*restrict map
)
167 for (unsigned i
= 0; i
< map
->channels
; i
++)
169 const unsigned pos
= map
->pos
[i
];
170 uint_fast16_t vlc_chan
= 0;
172 if (pos
< sizeof (vlc_chans
) / sizeof (vlc_chans
[0]))
173 vlc_chan
= vlc_chans
[pos
];
176 msg_Dbg (obj
, " %s channel %u position %u", "unsupported", i
, pos
);
181 msg_Dbg (obj
, " %s channel %u position %u", "duplicate", i
, pos
);
190 * Compares a fixed ALSA channels map with the VLC channels order.
192 static unsigned SetupChannelsFixed(const snd_pcm_chmap_t
*restrict map
,
193 uint16_t *restrict maskp
, uint8_t *restrict tab
)
195 uint32_t chans_out
[AOUT_CHAN_MAX
];
198 for (unsigned i
= 0; i
< map
->channels
; i
++)
200 uint_fast16_t vlc_chan
= vlc_chans
[map
->pos
[i
]];
202 chans_out
[i
] = vlc_chan
;
207 return aout_CheckChannelReorder(NULL
, chans_out
, mask
, tab
);
211 * Negotiate channels mapping.
213 static unsigned SetupChannels (vlc_object_t
*obj
, snd_pcm_t
*pcm
,
214 uint16_t *restrict mask
, uint8_t *restrict tab
)
216 snd_pcm_chmap_query_t
**maps
= snd_pcm_query_chmaps (pcm
);
218 { /* Fallback to default order if unknown */
219 msg_Dbg(obj
, "channels map not provided");
223 /* Find most appropriate available channels map */
224 unsigned best_offset
, best_score
= 0, to_reorder
= 0;
226 for (snd_pcm_chmap_query_t
*const *p
= maps
; *p
!= NULL
; p
++)
228 snd_pcm_chmap_query_t
*map
= *p
;
232 case SND_CHMAP_TYPE_FIXED
:
233 case SND_CHMAP_TYPE_PAIRED
:
234 case SND_CHMAP_TYPE_VAR
:
237 msg_Err (obj
, "unknown channels map type %u", map
->type
);
241 int chans
= Map2Mask (obj
, &map
->map
);
245 unsigned score
= (popcount (chans
& *mask
) << 8)
246 | (255 - popcount (chans
));
247 if (score
> best_score
)
249 best_offset
= p
- maps
;
256 msg_Err (obj
, "cannot find supported channels map");
260 const snd_pcm_chmap_t
*map
= &maps
[best_offset
]->map
;
261 msg_Dbg (obj
, "using channels map %u, type %u, %u channel(s)", best_offset
,
262 maps
[best_offset
]->type
, map
->channels
);
264 /* Setup channels map */
265 to_reorder
= SetupChannelsFixed(map
, mask
, tab
);
267 /* TODO: avoid reordering for PAIRED and VAR types */
268 //snd_pcm_set_chmap (pcm, ...)
270 snd_pcm_free_chmaps (maps
);
273 #else /* (SND_LIB_VERSION < 0x01001B) */
274 # define SetupChannels(obj, pcm, mask, tab) (0)
277 static int TimeGet (audio_output_t
*aout
, mtime_t
*);
278 static void Play (audio_output_t
*, block_t
*);
279 static void Pause (audio_output_t
*, bool, mtime_t
);
280 static void PauseDummy (audio_output_t
*, bool, mtime_t
);
281 static void Flush (audio_output_t
*, bool);
283 /** Initializes an ALSA playback stream */
284 static int Start (audio_output_t
*aout
, audio_sample_format_t
*restrict fmt
)
286 aout_sys_t
*sys
= aout
->sys
;
287 snd_pcm_format_t pcm_format
; /* ALSA sample format */
290 if (aout_FormatNbChannels(fmt
) == 0)
293 switch (fmt
->i_format
)
296 pcm_format
= SND_PCM_FORMAT_FLOAT64
;
299 pcm_format
= SND_PCM_FORMAT_FLOAT
;
302 pcm_format
= SND_PCM_FORMAT_S32
;
305 pcm_format
= SND_PCM_FORMAT_S16
;
308 pcm_format
= SND_PCM_FORMAT_U8
;
311 if (AOUT_FMT_SPDIF(fmt
))
312 spdif
= var_InheritBool (aout
, "spdif");
315 fmt
->i_format
= VLC_CODEC_SPDIFL
;
316 pcm_format
= SND_PCM_FORMAT_S16
;
321 fmt
->i_format
= VLC_CODEC_FL32
;
322 pcm_format
= SND_PCM_FORMAT_FLOAT
;
326 fmt
->i_format
= VLC_CODEC_S16N
;
327 pcm_format
= SND_PCM_FORMAT_S16
;
331 const char *device
= sys
->device
;
333 /* Choose the IEC device for S/PDIF output */
337 const char *opt
= NULL
;
339 if (!strcmp (device
, "default"))
340 device
= "iec958"; /* TODO: hdmi */
342 if (!strncmp (device
, "iec958", 6))
344 if (!strncmp (device
, "hdmi", 4))
350 case ':': sep
= ','; break;
351 case '\0': sep
= ':'; break;
363 case freq: aes3 = IEC958_AES3_CON_FS_ ## freq; break;
364 FS( 44100) /* def. */ FS( 48000) FS( 32000)
365 FS( 22050) FS( 24000)
366 FS( 88200) FS(768000) FS( 96000)
367 FS(176400) FS(192000)
370 aes3
= IEC958_AES3_CON_FS_NOTID
;
374 if (asprintf (&devbuf
, "%s%cAES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
376 IEC958_AES0_CON_EMPHASIS_NONE
| IEC958_AES0_NONAUDIO
,
377 IEC958_AES1_CON_ORIGINAL
| IEC958_AES1_CON_PCM_CODER
,
383 /* Open the device */
385 /* VLC always has a resampler. No need for ALSA's. */
386 const int mode
= SND_PCM_NO_AUTO_RESAMPLE
;
388 int val
= snd_pcm_open (&pcm
, device
, SND_PCM_STREAM_PLAYBACK
, mode
);
391 msg_Err (aout
, "cannot open ALSA device \"%s\": %s", device
,
393 vlc_dialog_display_error (aout
, _("Audio output failed"),
394 _("The audio device \"%s\" could not be used:\n%s."),
395 sys
->device
, snd_strerror (val
));
401 /* Print some potentially useful debug */
402 msg_Dbg (aout
, "using ALSA device: %s", device
);
404 DumpDevice (VLC_OBJECT(aout
), pcm
);
406 /* Get Initial hardware parameters */
407 snd_pcm_hw_params_t
*hw
;
410 snd_pcm_hw_params_alloca (&hw
);
411 snd_pcm_hw_params_any (pcm
, hw
);
412 Dump (aout
, "initial hardware setup:\n", snd_pcm_hw_params_dump
, hw
);
414 val
= snd_pcm_hw_params_set_rate_resample(pcm
, hw
, 0);
417 msg_Err (aout
, "cannot disable resampling: %s", snd_strerror (val
));
421 val
= snd_pcm_hw_params_set_access (pcm
, hw
,
422 SND_PCM_ACCESS_RW_INTERLEAVED
);
425 msg_Err (aout
, "cannot set access mode: %s", snd_strerror (val
));
429 /* Set sample format */
430 if (snd_pcm_hw_params_test_format (pcm
, hw
, pcm_format
) == 0)
433 if (snd_pcm_hw_params_test_format (pcm
, hw
, SND_PCM_FORMAT_FLOAT
) == 0)
435 fmt
->i_format
= VLC_CODEC_FL32
;
436 pcm_format
= SND_PCM_FORMAT_FLOAT
;
439 if (snd_pcm_hw_params_test_format (pcm
, hw
, SND_PCM_FORMAT_S32
) == 0)
441 fmt
->i_format
= VLC_CODEC_S32N
;
442 pcm_format
= SND_PCM_FORMAT_S32
;
445 if (snd_pcm_hw_params_test_format (pcm
, hw
, SND_PCM_FORMAT_S16
) == 0)
447 fmt
->i_format
= VLC_CODEC_S16N
;
448 pcm_format
= SND_PCM_FORMAT_S16
;
452 msg_Err (aout
, "no supported sample format");
456 val
= snd_pcm_hw_params_set_format (pcm
, hw
, pcm_format
);
459 msg_Err (aout
, "cannot set sample format: %s", snd_strerror (val
));
463 /* Set channels count */
467 uint16_t map
= var_InheritInteger (aout
, "alsa-audio-channels");
469 sys
->chans_to_reorder
= SetupChannels (VLC_OBJECT(aout
), pcm
, &map
,
471 fmt
->i_physical_channels
= map
;
472 channels
= popcount (map
);
476 sys
->chans_to_reorder
= 0;
480 /* By default, ALSA plug will pad missing channels with zeroes, which is
481 * usually fine. However, it will also discard extraneous channels, which
482 * is not acceptable. Thus the user must configure the physically
483 * available channels, and VLC will downmix if needed. */
484 val
= snd_pcm_hw_params_set_channels (pcm
, hw
, channels
);
487 msg_Err (aout
, "cannot set %u channels: %s", channels
,
492 /* Set sample rate */
493 val
= snd_pcm_hw_params_set_rate_near (pcm
, hw
, &fmt
->i_rate
, NULL
);
496 msg_Err (aout
, "cannot set sample rate: %s", snd_strerror (val
));
499 sys
->rate
= fmt
->i_rate
;
501 #if 1 /* work-around for period-long latency outputs (e.g. PulseAudio): */
502 param
= AOUT_MIN_PREPARE_TIME
;
503 val
= snd_pcm_hw_params_set_period_time_near (pcm
, hw
, ¶m
, NULL
);
506 msg_Err (aout
, "cannot set period: %s", snd_strerror (val
));
510 /* Set buffer size */
511 param
= AOUT_MAX_ADVANCE_TIME
;
512 val
= snd_pcm_hw_params_set_buffer_time_near (pcm
, hw
, ¶m
, NULL
);
515 msg_Err (aout
, "cannot set buffer duration: %s", snd_strerror (val
));
519 val
= snd_pcm_hw_params_get_buffer_time (hw
, ¶m
, NULL
);
522 msg_Warn (aout
, "cannot get buffer time: %s", snd_strerror(val
));
523 param
= AOUT_MIN_PREPARE_TIME
;
527 val
= snd_pcm_hw_params_set_period_time_near (pcm
, hw
, ¶m
, NULL
);
530 msg_Err (aout
, "cannot set period: %s", snd_strerror (val
));
535 /* Commit hardware parameters */
536 val
= snd_pcm_hw_params (pcm
, hw
);
539 msg_Err (aout
, "cannot commit hardware parameters: %s",
543 Dump (aout
, "final HW setup:\n", snd_pcm_hw_params_dump
, hw
);
545 /* Get Initial software parameters */
546 snd_pcm_sw_params_t
*sw
;
548 snd_pcm_sw_params_alloca (&sw
);
549 snd_pcm_sw_params_current (pcm
, sw
);
550 Dump (aout
, "initial software parameters:\n", snd_pcm_sw_params_dump
, sw
);
553 //snd_pcm_sw_params_set_avail_min( pcm, sw, i_period_size );
555 val
= snd_pcm_sw_params_set_start_threshold (pcm
, sw
, 1);
558 msg_Err( aout
, "unable to set start threshold (%s)",
559 snd_strerror( val
) );
564 /* Commit software parameters. */
565 val
= snd_pcm_sw_params (pcm
, sw
);
568 msg_Err (aout
, "cannot commit software parameters: %s",
572 Dump (aout
, "final software parameters:\n", snd_pcm_sw_params_dump
, sw
);
574 val
= snd_pcm_prepare (pcm
);
577 msg_Err (aout
, "cannot prepare device: %s", snd_strerror (val
));
581 /* Setup audio_output_t */
584 fmt
->i_bytes_per_frame
= AOUT_SPDIF_SIZE
;
585 fmt
->i_frame_length
= A52_FRAME_NB
;
587 fmt
->channel_type
= AUDIO_CHANNEL_TYPE_BITMAP
;
588 sys
->format
= fmt
->i_format
;
590 aout
->time_get
= TimeGet
;
592 if (snd_pcm_hw_params_can_pause (hw
))
596 aout
->pause
= PauseDummy
;
597 msg_Warn (aout
, "device cannot be paused");
600 aout_SoftVolumeStart (aout
);
608 static int TimeGet (audio_output_t
*aout
, mtime_t
*restrict delay
)
610 aout_sys_t
*sys
= aout
->sys
;
611 snd_pcm_sframes_t frames
;
613 int val
= snd_pcm_delay (sys
->pcm
, &frames
);
616 msg_Err (aout
, "cannot estimate delay: %s", snd_strerror (val
));
619 *delay
= frames
* CLOCK_FREQ
/ sys
->rate
;
624 * Queues one audio buffer to the hardware.
626 static void Play (audio_output_t
*aout
, block_t
*block
)
628 aout_sys_t
*sys
= aout
->sys
;
630 if (sys
->chans_to_reorder
!= 0)
631 aout_ChannelReorder(block
->p_buffer
, block
->i_buffer
,
632 sys
->chans_to_reorder
, sys
->chans_table
, sys
->format
);
634 snd_pcm_t
*pcm
= sys
->pcm
;
636 /* TODO: better overflow handling */
637 /* TODO: no period wake ups */
639 while (block
->i_nb_samples
> 0)
641 snd_pcm_sframes_t frames
;
643 frames
= snd_pcm_writei (pcm
, block
->p_buffer
, block
->i_nb_samples
);
646 size_t bytes
= snd_pcm_frames_to_bytes (pcm
, frames
);
647 block
->i_nb_samples
-= frames
;
648 block
->p_buffer
+= bytes
;
649 block
->i_buffer
-= bytes
;
654 int val
= snd_pcm_recover (pcm
, frames
, 1);
657 msg_Err (aout
, "cannot recover playback stream: %s",
659 DumpDeviceStatus (aout
, pcm
);
662 msg_Warn (aout
, "cannot write samples: %s", snd_strerror (frames
));
665 block_Release (block
);
669 * Pauses/resumes the audio playback.
671 static void Pause (audio_output_t
*aout
, bool pause
, mtime_t date
)
673 snd_pcm_t
*pcm
= aout
->sys
->pcm
;
675 int val
= snd_pcm_pause (pcm
, pause
);
677 PauseDummy (aout
, pause
, date
);
680 static void PauseDummy (audio_output_t
*aout
, bool pause
, mtime_t date
)
682 snd_pcm_t
*pcm
= aout
->sys
->pcm
;
684 /* Stupid device cannot pause. Discard samples. */
688 snd_pcm_prepare (pcm
);
693 * Flushes/drains the audio playback buffer.
695 static void Flush (audio_output_t
*aout
, bool wait
)
697 snd_pcm_t
*pcm
= aout
->sys
->pcm
;
703 snd_pcm_prepare (pcm
);
708 * Releases the audio output.
710 static void Stop (audio_output_t
*aout
)
712 aout_sys_t
*sys
= aout
->sys
;
713 snd_pcm_t
*pcm
= sys
->pcm
;
720 * Enumerates ALSA output devices.
722 static int EnumDevices(vlc_object_t
*obj
, char const *varname
,
723 char ***restrict idp
, char ***restrict namep
)
727 msg_Dbg (obj
, "Available ALSA PCM devices:");
728 if (snd_device_name_hint(-1, "pcm", &hints
) < 0)
731 char **ids
= NULL
, **names
= NULL
;
733 bool hinted_default
= false;
735 for (size_t i
= 0; hints
[i
] != NULL
; i
++)
737 void *hint
= hints
[i
];
739 char *name
= snd_device_name_get_hint(hint
, "NAME");
740 if (unlikely(name
== NULL
))
743 char *desc
= snd_device_name_get_hint(hint
, "DESC");
745 desc
= xstrdup (name
);
746 for (char *lf
= strchr(desc
, '\n'); lf
; lf
= strchr(lf
, '\n'))
748 msg_Dbg (obj
, "%s (%s)", (desc
!= NULL
) ? desc
: name
, name
);
750 ids
= xrealloc (ids
, (n
+ 1) * sizeof (*ids
));
751 names
= xrealloc (names
, (n
+ 1) * sizeof (*names
));
756 if (!strcmp(name
, "default"))
757 hinted_default
= true;
760 snd_device_name_free_hint(hints
);
764 ids
= xrealloc (ids
, (n
+ 1) * sizeof (*ids
));
765 names
= xrealloc (names
, (n
+ 1) * sizeof (*names
));
766 ids
[n
] = xstrdup ("default");
767 names
[n
] = xstrdup (_("Default"));
777 static int DeviceSelect (audio_output_t
*aout
, const char *id
)
779 aout_sys_t
*sys
= aout
->sys
;
781 char *device
= strdup (id
? id
: "default");
782 if (unlikely(device
== NULL
))
786 sys
->device
= device
;
787 aout_DeviceReport (aout
, device
);
788 aout_RestartRequest (aout
, AOUT_RESTART_OUTPUT
);
792 static int Open(vlc_object_t
*obj
)
794 audio_output_t
*aout
= (audio_output_t
*)obj
;
795 aout_sys_t
*sys
= malloc (sizeof (*sys
));
797 if (unlikely(sys
== NULL
))
799 sys
->device
= var_InheritString (aout
, "alsa-audio-device");
800 if (unlikely(sys
->device
== NULL
))
806 aout_SoftVolumeInit (aout
);
807 aout
->device_select
= DeviceSelect
;
808 aout_DeviceReport (aout
, sys
->device
);
810 /* ALSA does not support hot-plug events so list devices at startup */
812 int count
= EnumDevices (VLC_OBJECT(aout
), NULL
, &ids
, &names
);
815 for (int i
= 0; i
< count
; i
++)
817 aout_HotplugReport (aout
, ids
[i
], names
[i
]);
831 static void Close(vlc_object_t
*obj
)
833 audio_output_t
*aout
= (audio_output_t
*)obj
;
834 aout_sys_t
*sys
= aout
->sys
;