static int Open ( vlc_object_t *p_this ) { audio_output_t *p_aout = (audio_output_t *)p_this; struct aout_sys_t *p_sys = malloc(sizeof(aout_sys_t)); p_aout->sys = p_sys; OSStatus status = 0; // Setup the audio device. AudioStreamBasicDescription deviceFormat; deviceFormat.mSampleRate = 44100; deviceFormat.mFormatID = kAudioFormatLinearPCM; deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; // Signed integer, little endian deviceFormat.mBytesPerPacket = 4; deviceFormat.mFramesPerPacket = 1; deviceFormat.mBytesPerFrame = 4; deviceFormat.mChannelsPerFrame = 2; deviceFormat.mBitsPerChannel = 16; deviceFormat.mReserved = 0; // Create a new output AudioQueue for the device. status = AudioQueueNewOutput(&deviceFormat, // Format AudioQueueCallback, // Callback p_aout, // User data, passed to the callback CFRunLoopGetMain(), // RunLoop kCFRunLoopDefaultMode, // RunLoop mode 0, // Flags ; must be zero (per documentation)... &(p_sys->audioQueue)); // Output // This will be used for boosting the audio without the need of a mixer (floating-point conversion is expensive on ARM) // AudioQueueSetParameter(p_sys->audioQueue, kAudioQueueParam_Volume, 12.0); // Defaults to 1.0 msg_Dbg(p_aout, "New AudioQueue output created (status = %i)", status); // Allocate buffers for the AudioQueue, and pre-fill them. for (int i = 0; i < NUMBER_OF_BUFFERS; ++i) { AudioQueueBufferRef buffer = NULL; status = AudioQueueAllocateBuffer(p_sys->audioQueue, FRAME_SIZE * 4, &buffer); AudioQueueCallback(NULL, p_sys->audioQueue, buffer); } /* Volume is entirely done in software. */ aout_SoftVolumeInit( p_aout ); p_aout->format.i_format = VLC_CODEC_S16L; p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; p_aout->format.i_rate = 44100; aout_PacketInit(p_aout, &p_sys->packet, FRAME_SIZE); p_aout->pf_play = aout_PacketPlay; p_aout->pf_pause = aout_PacketPause; p_aout->pf_flush = aout_PacketFlush; msg_Dbg(p_aout, "Starting AudioQueue (status = %i)", status); status = AudioQueueStart(p_sys->audioQueue, NULL); return VLC_SUCCESS; }
/***************************************************************************** * Open: create a JACK client *****************************************************************************/ static int Open( vlc_object_t *p_this ) { char psz_name[32]; audio_output_t *p_aout = (audio_output_t *)p_this; struct aout_sys_t *p_sys = NULL; int status = VLC_SUCCESS; unsigned int i; int i_error; /* Allocate structure */ p_sys = calloc( 1, sizeof( aout_sys_t ) ); if( p_sys == NULL ) { status = VLC_ENOMEM; goto error_out; } p_aout->sys = p_sys; p_sys->latency = 0; /* Connect to the JACK server */ snprintf( psz_name, sizeof(psz_name), "vlc_%d", getpid()); psz_name[sizeof(psz_name) - 1] = '\0'; p_sys->p_jack_client = jack_client_open( psz_name, JackNullOption | JackNoStartServer, NULL ); if( p_sys->p_jack_client == NULL ) { msg_Err( p_aout, "failed to connect to JACK server" ); status = VLC_EGENERIC; goto error_out; } /* Set the process callback */ jack_set_process_callback( p_sys->p_jack_client, Process, p_aout ); jack_set_graph_order_callback ( p_sys->p_jack_client, GraphChange, p_aout ); p_aout->pf_play = aout_PacketPlay; p_aout->pf_pause = aout_PacketPause; p_aout->pf_flush = aout_PacketFlush; aout_PacketInit( p_aout, &p_sys->packet, jack_get_buffer_size( p_sys->p_jack_client ) ); aout_VolumeSoftInit( p_aout ); /* JACK only supports fl32 format */ p_aout->format.i_format = VLC_CODEC_FL32; // TODO add buffer size callback p_aout->format.i_rate = jack_get_sample_rate( p_sys->p_jack_client ); p_sys->i_channels = aout_FormatNbChannels( &p_aout->format ); p_sys->p_jack_ports = malloc( p_sys->i_channels * sizeof(jack_port_t *) ); if( p_sys->p_jack_ports == NULL ) { status = VLC_ENOMEM; goto error_out; } p_sys->p_jack_buffers = malloc( p_sys->i_channels * sizeof(jack_sample_t *) ); if( p_sys->p_jack_buffers == NULL ) { status = VLC_ENOMEM; goto error_out; } /* Create the output ports */ for( i = 0; i < p_sys->i_channels; i++ ) { snprintf( psz_name, sizeof(psz_name), "out_%d", i + 1); psz_name[sizeof(psz_name) - 1] = '\0'; p_sys->p_jack_ports[i] = jack_port_register( p_sys->p_jack_client, psz_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); if( p_sys->p_jack_ports[i] == NULL ) { msg_Err( p_aout, "failed to register a JACK port" ); status = VLC_EGENERIC; goto error_out; } } /* Tell the JACK server we are ready */ i_error = jack_activate( p_sys->p_jack_client ); if( i_error ) { msg_Err( p_aout, "failed to activate JACK client (error %d)", i_error ); status = VLC_EGENERIC; goto error_out; } /* Auto connect ports if we were asked to */ if( var_InheritBool( p_aout, AUTO_CONNECT_OPTION ) ) { unsigned int i_in_ports; char *psz_regex = var_InheritString( p_aout, CONNECT_REGEX_OPTION ); const char **pp_in_ports = jack_get_ports( p_sys->p_jack_client, psz_regex, NULL, JackPortIsInput ); free( psz_regex ); /* Count the number of returned ports */ i_in_ports = 0; while( pp_in_ports && pp_in_ports[i_in_ports] ) { i_in_ports++; } /* Tie the output ports to JACK input ports */ for( i = 0; i_in_ports > 0 && i < p_sys->i_channels; i++ ) { const char* psz_in = pp_in_ports[i % i_in_ports]; const char* psz_out = jack_port_name( p_sys->p_jack_ports[i] ); i_error = jack_connect( p_sys->p_jack_client, psz_out, psz_in ); if( i_error ) { msg_Err( p_aout, "failed to connect port %s to port %s (error %d)", psz_out, psz_in, i_error ); } else { msg_Dbg( p_aout, "connecting port %s to port %s", psz_out, psz_in ); } } free( pp_in_ports ); } msg_Dbg( p_aout, "JACK audio output initialized (%d channels, rate=%d)", p_sys->i_channels, p_aout->format.i_rate ); error_out: /* Clean up, if an error occurred */ if( status != VLC_SUCCESS && p_sys != NULL) { if( p_sys->p_jack_client ) { jack_deactivate( p_sys->p_jack_client ); jack_client_close( p_sys->p_jack_client ); aout_PacketDestroy( p_aout ); } free( p_sys->p_jack_ports ); free( p_sys->p_jack_buffers ); free( p_sys ); } return status; }
/***************************************************************************** * Open: open the audio device *****************************************************************************/ static int Start ( audio_output_t *p_aout, audio_sample_format_t *fmt ) { aout_sys_t *p_sys = p_aout->sys; char *psz_mode; ULONG i_kai_mode; KAISPEC ks_wanted, ks_obtained; int i_nb_channels; int i_bytes_per_frame; vlc_value_t val, text; audio_sample_format_t format = *fmt; if( var_Get( p_aout, "audio-device", &val ) != VLC_ENOVAR ) { /* The user has selected an audio device. */ if ( val.i_int == AOUT_VAR_STEREO ) { format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; } else if ( val.i_int == AOUT_VAR_MONO ) { format.i_physical_channels = AOUT_CHAN_CENTER; } } psz_mode = var_InheritString( p_aout, "kai-audio-device" ); if( !psz_mode ) psz_mode = ( char * )ppsz_kai_audio_device[ 0 ]; // "auto" i_kai_mode = KAIM_AUTO; if( strcmp( psz_mode, "dart" ) == 0 ) i_kai_mode = KAIM_DART; else if( strcmp( psz_mode, "uniaud" ) == 0 ) i_kai_mode = KAIM_UNIAUD; msg_Dbg( p_aout, "selected mode = %s", psz_mode ); if( psz_mode != ppsz_kai_audio_device[ 0 ]) free( psz_mode ); i_nb_channels = aout_FormatNbChannels( &format ); if ( i_nb_channels > 2 ) { /* KAI doesn't support more than two channels. */ i_nb_channels = 2; format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; } /* Support s16l only */ format.i_format = VLC_CODEC_S16L; aout_FormatPrepare( &format ); i_bytes_per_frame = format.i_bytes_per_frame; /* Initialize library */ if( kaiInit( i_kai_mode )) { msg_Err( p_aout, "cannot initialize KAI"); return VLC_EGENERIC; } ks_wanted.usDeviceIndex = 0; ks_wanted.ulType = KAIT_PLAY; ks_wanted.ulBitsPerSample = BPS_16; ks_wanted.ulSamplingRate = format.i_rate; ks_wanted.ulDataFormat = MCI_WAVE_FORMAT_PCM; ks_wanted.ulChannels = i_nb_channels; ks_wanted.ulNumBuffers = 2; ks_wanted.ulBufferSize = FRAME_SIZE * i_bytes_per_frame; ks_wanted.fShareable = !var_InheritBool( p_aout, "kai-audio-exclusive-mode"); ks_wanted.pfnCallBack = KaiCallback; ks_wanted.pCallBackData = p_aout; msg_Dbg( p_aout, "requested ulBufferSize = %ld", ks_wanted.ulBufferSize ); /* Open the sound device. */ if( kaiOpen( &ks_wanted, &ks_obtained, &p_sys->hkai )) { msg_Err( p_aout, "cannot open KAI device"); goto exit_kai_done; } msg_Dbg( p_aout, "open in %s mode", ks_obtained.fShareable ? "shareable" : "exclusive" ); msg_Dbg( p_aout, "obtained i_nb_samples = %lu", ks_obtained.ulBufferSize / i_bytes_per_frame ); msg_Dbg( p_aout, "obtained i_bytes_per_frame = %d", format.i_bytes_per_frame ); p_sys->format = *fmt = format; p_aout->time_get = aout_PacketTimeGet; p_aout->play = Play; p_aout->pause = NULL; p_aout->flush = aout_PacketFlush; aout_SoftVolumeStart( p_aout ); aout_PacketInit( p_aout, &p_sys->packet, ks_obtained.ulBufferSize / i_bytes_per_frame, &format ); if ( var_Type( p_aout, "audio-device" ) == 0 ) { /* First launch. */ var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); text.psz_string = _("Audio Device"); var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL ); val.i_int = AOUT_VAR_STEREO; text.psz_string = _("Stereo"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_MONO; text.psz_string = _("Mono"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); if ( i_nb_channels == 2 ) { val.i_int = AOUT_VAR_STEREO; } else { val.i_int = AOUT_VAR_MONO; } var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL ); var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL ); } /* Prevent SIG_FPE */ _control87(MCW_EM, MCW_EM); return VLC_SUCCESS; exit_kai_done : kaiDone(); return VLC_EGENERIC; }
/***************************************************************************** * Open: open the audio device (the digital sound processor) ***************************************************************************** * This function opens the DSP as a usual non-blocking write-only file, and * modifies the p_aout->p_sys->i_fd with the file's descriptor. *****************************************************************************/ static int Open( vlc_object_t *p_this ) { audio_output_t * p_aout = (audio_output_t *)p_this; struct aout_sys_t * p_sys; char * psz_device; vlc_value_t val; /* Allocate structure */ p_aout->sys = p_sys = malloc( sizeof( aout_sys_t ) ); if( p_sys == NULL ) return VLC_ENOMEM; /* Get device name */ if( (psz_device = var_InheritString( p_aout, "oss-audio-device" )) == NULL ) { msg_Err( p_aout, "no audio device specified (maybe /dev/dsp?)" ); free( p_sys ); return VLC_EGENERIC; } /* Open the sound device in non-blocking mode, because ALSA's OSS * emulation and some broken OSS drivers would make a blocking call * wait forever until the device is available. Since this breaks the * OSS spec, we immediately put it back to blocking mode if the * operation was successful. */ p_sys->i_fd = vlc_open( psz_device, O_WRONLY | O_NDELAY ); if( p_sys->i_fd < 0 ) { msg_Err( p_aout, "cannot open audio device (%s)", psz_device ); free( psz_device ); free( p_sys ); return VLC_EGENERIC; } /* if the opening was ok, put the device back in blocking mode */ fcntl( p_sys->i_fd, F_SETFL, fcntl( p_sys->i_fd, F_GETFL ) &~ FNDELAY ); free( psz_device ); p_aout->pf_play = aout_PacketPlay; p_aout->pf_pause = aout_PacketPause; p_aout->pf_flush = aout_PacketFlush; if ( var_Type( p_aout, "audio-device" ) == 0 ) Probe( p_aout ); var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL ); if ( var_Get( p_aout, "audio-device", &val ) < 0 ) /* Probe() has failed. */ goto error; if ( val.i_int == AOUT_VAR_SPDIF ) { p_aout->format.i_format = VLC_CODEC_SPDIFL; } else if ( val.i_int == AOUT_VAR_5_1 ) { p_aout->format.i_format = VLC_CODEC_S16N; p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; } else if ( val.i_int == AOUT_VAR_2F2R ) { p_aout->format.i_format = VLC_CODEC_S16N; p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; } else if ( val.i_int == AOUT_VAR_STEREO ) { p_aout->format.i_format = VLC_CODEC_S16N; p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; } else if ( val.i_int == AOUT_VAR_MONO ) { p_aout->format.i_format = VLC_CODEC_S16N; p_aout->format.i_physical_channels = AOUT_CHAN_CENTER; } else { /* This should not happen ! */ msg_Err( p_aout, "internal: can't find audio-device (%"PRId64")", val.i_int ); goto error; } var_TriggerCallback( p_aout, "intf-change" ); /* Reset the DSP device */ if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ) { msg_Err( p_aout, "cannot reset OSS audio device" ); goto error; } /* Set the output format */ if ( AOUT_FMT_SPDIF( &p_aout->format ) ) { int i_format = AFMT_AC3; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 || i_format != AFMT_AC3 ) { msg_Err( p_aout, "cannot reset OSS audio device" ); goto error; } p_aout->format.i_format = VLC_CODEC_SPDIFL; p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE; p_aout->format.i_frame_length = A52_FRAME_NB; aout_PacketInit( p_aout, &p_sys->packet, A52_FRAME_NB ); aout_VolumeNoneInit( p_aout ); } else { unsigned int i_format = AFMT_S16_NE; unsigned int i_frame_size, i_fragments; unsigned int i_rate; unsigned int i_nb_channels; audio_buf_info audio_buf; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 ) { msg_Err( p_aout, "cannot set audio output format" ); goto error; } switch ( i_format ) { case AFMT_U8: p_aout->format.i_format = VLC_CODEC_U8; break; case AFMT_S8: p_aout->format.i_format = VLC_CODEC_S8; break; case AFMT_U16_LE: p_aout->format.i_format = VLC_CODEC_U16L; break; case AFMT_S16_LE: p_aout->format.i_format = VLC_CODEC_S16L; break; case AFMT_U16_BE: p_aout->format.i_format = VLC_CODEC_U16B; break; case AFMT_S16_BE: p_aout->format.i_format = VLC_CODEC_S16B; break; default: msg_Err( p_aout, "OSS fell back to an unknown format (%d)", i_format ); goto error; } i_nb_channels = aout_FormatNbChannels( &p_aout->format ); /* Set the number of channels */ if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) < 0 || i_nb_channels != aout_FormatNbChannels( &p_aout->format ) ) { msg_Err( p_aout, "cannot set number of audio channels (%s)", aout_FormatPrintChannels( &p_aout->format) ); goto error; } /* Set the output rate */ i_rate = p_aout->format.i_rate; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SPEED, &i_rate ) < 0 ) { msg_Err( p_aout, "cannot set audio output rate (%i)", p_aout->format.i_rate ); goto error; } if( i_rate != p_aout->format.i_rate ) { p_aout->format.i_rate = i_rate; } /* Set the fragment size */ aout_FormatPrepare( &p_aout->format ); /* i_fragment = xxxxyyyy where: xxxx is fragtotal * 1 << yyyy is fragsize */ i_frame_size = ((uint64_t)p_aout->format.i_bytes_per_frame * p_aout->format.i_rate * 65536) / (48000 * 2 * 2) / FRAME_COUNT; i_fragments = 4; while( i_fragments < 12 && (1U << i_fragments) < i_frame_size ) { ++i_fragments; } i_fragments |= FRAME_COUNT << 16; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFRAGMENT, &i_fragments ) < 0 ) { msg_Warn( p_aout, "cannot set fragment size (%.8x)", i_fragments ); } if( ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ) < 0 ) { msg_Err( p_aout, "cannot get fragment size" ); goto error; } /* Number of fragments actually allocated */ p_aout->sys->i_fragstotal = audio_buf.fragstotal; /* Maximum duration the soundcard's buffer can hold */ p_aout->sys->max_buffer_duration = (mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000 / p_aout->format.i_bytes_per_frame / p_aout->format.i_rate * p_aout->format.i_frame_length; aout_PacketInit( p_aout, &p_sys->packet, audio_buf.fragsize/p_aout->format.i_bytes_per_frame ); aout_VolumeSoftInit( p_aout ); } /* Create OSS thread and wait for its readiness. */ if( vlc_clone( &p_sys->thread, OSSThread, p_aout, VLC_THREAD_PRIORITY_OUTPUT ) ) { msg_Err( p_aout, "cannot create OSS thread (%m)" ); aout_PacketDestroy( p_aout ); goto error; } return VLC_SUCCESS; error: var_DelCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL ); close( p_sys->i_fd ); free( p_sys ); return VLC_EGENERIC; }
static int PAOpenStream( audio_output_t *p_aout ) { aout_sys_t *p_sys = p_aout->sys; const PaHostErrorInfo* paLastHostErrorInfo = Pa_GetLastHostErrorInfo(); PaStreamParameters paStreamParameters; vlc_value_t val; int i_channels, i_err; uint32_t i_channel_mask; if( var_Get( p_aout, "audio-device", &val ) < 0 ) { return VLC_EGENERIC; } if( val.i_int == AOUT_VAR_5_1 ) { p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; } else if( val.i_int == AOUT_VAR_3F2R ) { p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; } else if( val.i_int == AOUT_VAR_2F2R ) { p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; } else if( val.i_int == AOUT_VAR_MONO ) { p_aout->format.i_physical_channels = AOUT_CHAN_CENTER; } else { p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; } i_channels = aout_FormatNbChannels( &p_aout->format ); msg_Dbg( p_aout, "nb_channels requested = %d", i_channels ); i_channel_mask = p_aout->format.i_physical_channels; /* Calculate the frame size in bytes */ p_sys->i_sample_size = 4 * i_channels; aout_FormatPrepare( &p_aout->format ); aout_PacketInit( p_aout, &p_sys->packet, FRAME_SIZE ); aout_VolumeSoftInit( p_aout ); /* Check for channel reordering */ p_aout->sys->i_channel_mask = i_channel_mask; p_aout->sys->i_bits_per_sample = 32; /* forced to paFloat32 */ p_aout->sys->i_channels = i_channels; p_aout->sys->b_chan_reorder = aout_CheckChannelReorder( NULL, pi_channels_out, i_channel_mask, i_channels, p_aout->sys->pi_chan_table ); if( p_aout->sys->b_chan_reorder ) { msg_Dbg( p_aout, "channel reordering needed" ); } paStreamParameters.device = p_sys->i_device_id; paStreamParameters.channelCount = i_channels; paStreamParameters.sampleFormat = paFloat32; paStreamParameters.suggestedLatency = p_sys->deviceInfo->defaultLowOutputLatency; paStreamParameters.hostApiSpecificStreamInfo = NULL; i_err = Pa_OpenStream( &p_sys->p_stream, NULL /* no input */, &paStreamParameters, (double)p_aout->format.i_rate, FRAME_SIZE, paClipOff, paCallback, p_sys ); if( i_err != paNoError ) { msg_Err( p_aout, "Pa_OpenStream returns %d : %s", i_err, Pa_GetErrorText( i_err ) ); if( i_err == paUnanticipatedHostError ) { msg_Err( p_aout, "type %d code %ld : %s", paLastHostErrorInfo->hostApiType, paLastHostErrorInfo->errorCode, paLastHostErrorInfo->errorText ); } p_sys->p_stream = 0; aout_PacketDestroy( p_aout ); return VLC_EGENERIC; } i_err = Pa_StartStream( p_sys->p_stream ); if( i_err != paNoError ) { msg_Err( p_aout, "Pa_StartStream() failed" ); Pa_CloseStream( p_sys->p_stream ); aout_PacketDestroy( p_aout ); return VLC_EGENERIC; } return VLC_SUCCESS; }