/*---------------------------------------------------------------------- | AlsaOutput_Close +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Close(AlsaOutput* self) { ATX_LOG_FINER("closing output"); switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: /* ignore */ return BLT_SUCCESS; case BLT_ALSA_OUTPUT_STATE_PREPARED: /* wait for buffers to finish */ ATX_LOG_FINER("snd_pcm_drain"); snd_pcm_drain(self->device_handle); /* FALLTHROUGH */ case BLT_ALSA_OUTPUT_STATE_OPEN: case BLT_ALSA_OUTPUT_STATE_CONFIGURED: /* close the device */ ATX_LOG_FINER("snd_pcm_close"); snd_pcm_close(self->device_handle); self->device_handle = NULL; break; } /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_CLOSED); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | OsxAudioUnitsOutput_Drain +---------------------------------------------------------------------*/ BLT_METHOD OsxAudioUnitsOutput_Drain(BLT_OutputNode* _self) { OsxAudioUnitsOutput* self = ATX_SELF(OsxAudioUnitsOutput, BLT_OutputNode); unsigned int watchdog = 20000000/BLT_OSX_AUDIO_UNITS_OUTPUT_SLEEP_INTERVAL; ATX_LOG_FINER("draining packets"); /* lock the queue */ pthread_mutex_lock(&self->lock); /* wait until there are no more packets in the queue */ while (ATX_List_GetItemCount(self->packet_queue)) { pthread_mutex_unlock(&self->lock); ATX_LOG_FINER("waiting..."); usleep(BLT_OSX_AUDIO_UNITS_OUTPUT_SLEEP_INTERVAL); pthread_mutex_lock(&self->lock); if (--watchdog == 0) { ATX_LOG_WARNING("*** the watchdog bit us ***"); break; } } /* unlock the queue */ pthread_mutex_unlock(&self->lock); ATX_LOG_FINER("end"); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | WaveFormatter_Deactivate +---------------------------------------------------------------------*/ BLT_METHOD WaveFormatter_Deactivate(BLT_MediaNode* _self) { WaveFormatter* self = ATX_SELF_EX(WaveFormatter, BLT_BaseMediaNode, BLT_MediaNode); ATX_LOG_FINER("WaveFormatter::Deactivate"); /* update the header if needed */ if (self->output.stream) { ATX_Position where = 0; ATX_OutputStream_Tell(self->output.stream, &where); self->input.size = where; if (self->input.size >= BLT_WAVE_FORMATTER_RIFF_HEADER_SIZE) { self->input.size -= BLT_WAVE_FORMATTER_RIFF_HEADER_SIZE; } /* update the header */ WaveFormatter_UpdateWavHeader(self); /* set the stream back to its original position */ ATX_OutputStream_Seek(self->output.stream, where); } /* release the output stream */ ATX_RELEASE_OBJECT(self->output.stream); /* call the base class method */ BLT_BaseMediaNode_Deactivate(_self); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | SilenceRemover_TrimPending +---------------------------------------------------------------------*/ static void SilenceRemover_TrimPending(SilenceRemover* self) { BLT_MediaPacket* packet = self->input.pending; short* pcm; BLT_Cardinal sample_count; BLT_Cardinal skip = 0; int sample; /* quick check */ if (!packet) return; ATX_LOG_FINER("SilenceRemover: trimming pending packet"); /* remove silence at the end of the packet */ pcm = (short*)BLT_MediaPacket_GetPayloadBuffer(packet); sample_count = BLT_MediaPacket_GetPayloadSize(packet)/4; pcm += sample_count*2; for (sample = sample_count-1; sample >= 0; sample--, pcm-=2) { if (pcm[0] > -BLT_SILENCE_REMOVER_THRESHOLD && pcm[0] < BLT_SILENCE_REMOVER_THRESHOLD && pcm[1] > -BLT_SILENCE_REMOVER_THRESHOLD && pcm[1] < BLT_SILENCE_REMOVER_THRESHOLD) { skip++; } } BLT_MediaPacket_SetPayloadSize(packet, (sample_count-skip)*4); }
/*---------------------------------------------------------------------- | AlsaOutput_Unprepare +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Unprepare(AlsaOutput* self) { BLT_Result result; ATX_LOG_FINER("unpreparing output"); switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: case BLT_ALSA_OUTPUT_STATE_OPEN: case BLT_ALSA_OUTPUT_STATE_CONFIGURED: /* ignore */ break; case BLT_ALSA_OUTPUT_STATE_PREPARED: /* drain any pending samples */ result = AlsaOutput_Drain(self); if (BLT_FAILED(result)) return result; /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_CONFIGURED); break; } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Open +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Open(AlsaOutput* self) { int io_result; ATX_LOG_FINE_1("openning output - name=%s", ATX_CSTR(self->device_name)); switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: ATX_LOG_FINER("snd_pcm_open"); io_result = snd_pcm_open(&self->device_handle, ATX_CSTR(self->device_name), SND_PCM_STREAM_PLAYBACK, 0); if (io_result != 0) { self->device_handle = NULL; return BLT_FAILURE; } break; case BLT_ALSA_OUTPUT_STATE_OPEN: /* ignore */ return BLT_SUCCESS; case BLT_ALSA_OUTPUT_STATE_CONFIGURED: case BLT_ALSA_OUTPUT_STATE_PREPARED: return BLT_FAILURE; } /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_OPEN); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | BLT_DecoderServer::SetInput +---------------------------------------------------------------------*/ BLT_Result BLT_DecoderServer::SetInput(BLT_CString name, BLT_CString type) { ATX_LOG_FINER("set-input"); return PostMessage( new BLT_DecoderServer_SetInputCommandMessage(name, type)); }
/*---------------------------------------------------------------------- | AlsaOutput_Prepare +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Prepare(AlsaOutput* self) { int ior; switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: case BLT_ALSA_OUTPUT_STATE_OPEN: /* we need to be configured already for 'prepare' to work */ return BLT_FAILURE; case BLT_ALSA_OUTPUT_STATE_CONFIGURED: /* prepare the device */ ATX_LOG_FINER("snd_pcm_prepare"); ior = snd_pcm_prepare(self->device_handle); if (ior != 0) { ATX_LOG_FINER_2("snd_pcm_prepare() failed (%d : %s)", ior, snd_strerror(ior)); return BLT_FAILURE; } break; case BLT_ALSA_OUTPUT_STATE_PREPARED: /* ignore */ return BLT_SUCCESS; } /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_PREPARED); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | SilenceRemover_AcceptPending +---------------------------------------------------------------------*/ static void SilenceRemover_AcceptPending(SilenceRemover* self) { BLT_MediaPacket* packet = self->input.pending; BLT_Result result; if (packet != NULL) { result = ATX_List_AddData(self->output.packets, packet); if (ATX_FAILED(result)) { BLT_MediaPacket_Release(packet); } self->input.pending = NULL; ATX_LOG_FINER("SilenceRemover: accepting pending packet"); } else { ATX_LOG_FINER("SilenceRemover: no pending packet"); } }
/*---------------------------------------------------------------------- | OsxAudioUnitsOutput_GetStatus +---------------------------------------------------------------------*/ BLT_METHOD OsxAudioUnitsOutput_GetStatus(BLT_OutputNode* _self, BLT_OutputNodeStatus* status) { OsxAudioUnitsOutput* self = ATX_SELF(OsxAudioUnitsOutput, BLT_OutputNode); /* default values */ status->flags = 0; pthread_mutex_lock(&self->lock); /* check if the queue is full */ if (ATX_List_GetItemCount(self->packet_queue) >= self->max_packets_in_queue) { ATX_LOG_FINER("packet queue is full"); status->flags |= BLT_OUTPUT_NODE_STATUS_QUEUE_FULL; } /* compute the media time */ BLT_TimeStamp_Set(status->media_time, 0, 0); if (self->media_time_snapshot.rendered_host_time) { UInt64 host_time = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); UInt64 media_time = BLT_TimeStamp_ToNanos(self->media_time_snapshot.rendered_packet_ts); ATX_LOG_FINER_3("host time = %lld, last rendered packet = %lld, rendered ts = %lld", host_time, self->media_time_snapshot.rendered_host_time, media_time); if (host_time > self->media_time_snapshot.rendered_host_time) { media_time += host_time-self->media_time_snapshot.rendered_host_time; } UInt64 max_media_time; max_media_time = BLT_TimeStamp_ToNanos(self->media_time_snapshot.rendered_packet_ts) + BLT_TimeStamp_ToNanos(self->media_time_snapshot.rendered_duration); ATX_LOG_FINER_2("computed media time = %lld, max media time = %lld", media_time, max_media_time); if (media_time > max_media_time) { ATX_LOG_FINER("media time clamped to max"); media_time = max_media_time; } status->media_time = BLT_TimeStamp_FromNanos(media_time); ATX_LOG_FINER_1("media time = %lld", media_time); } pthread_mutex_unlock(&self->lock); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | CrossFaderOutputPort_GetPacket +---------------------------------------------------------------------*/ BLT_METHOD CrossFaderOutputPort_GetPacket(BLT_PacketProducerInstance* instance, BLT_MediaPacket** packet) { CrossFader* fader = (CrossFader*)instance; ATX_ListItem* item; item = ATX_List_GetFirstItem(fader->output.packets); if (item) { *packet = ATX_ListItem_GetData(item); ATX_List_RemoveItem(fader->output.packets, item); ATX_LOG_FINER("CrossFaderInputPort_GetPacket - got one"); return BLT_SUCCESS; } else { *packet = NULL; ATX_LOG_FINER("CrossFaderInputPort_GetPacket - no more data"); return BLT_ERROR_PORT_HAS_NO_DATA; } }
/*---------------------------------------------------------------------- | SilenceRemover_HoldPacket +---------------------------------------------------------------------*/ static void SilenceRemover_HoldPacket(SilenceRemover* self, BLT_MediaPacket* packet) { ATX_LOG_FINER("SilenceRemover: holding packet"); /* accept the previously pending packet if any */ SilenceRemover_AcceptPending(self); /* hold the packet as a pending input */ self->input.pending = packet; BLT_MediaPacket_AddReference(packet); }
/*---------------------------------------------------------------------- | AlsaOutput_Deactivate +---------------------------------------------------------------------*/ BLT_METHOD AlsaOutput_Deactivate(BLT_MediaNode* _self) { AlsaOutput* self = ATX_SELF_EX(AlsaOutput, BLT_BaseMediaNode, BLT_MediaNode); ATX_LOG_FINER("deactivating output"); /* close the device */ AlsaOutput_Close(self); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Stop +---------------------------------------------------------------------*/ BLT_METHOD AlsaOutput_Stop(BLT_MediaNode* _self) { AlsaOutput* self = ATX_SELF_EX(AlsaOutput, BLT_BaseMediaNode, BLT_MediaNode); ATX_LOG_FINER("stopping output"); /* reset the device */ AlsaOutput_Reset(self); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Reset +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Reset(AlsaOutput* self) { ATX_LOG_FINER("resetting output"); switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: case BLT_ALSA_OUTPUT_STATE_OPEN: case BLT_ALSA_OUTPUT_STATE_CONFIGURED: /* ignore */ return BLT_SUCCESS; case BLT_ALSA_OUTPUT_STATE_PREPARED: ATX_LOG_FINER("snd_pcm_drop"); snd_pcm_drop(self->device_handle); AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_CONFIGURED); break; } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Drain +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Drain(AlsaOutput* self) { ATX_LOG_FINER("draining output"); switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: case BLT_ALSA_OUTPUT_STATE_OPEN: case BLT_ALSA_OUTPUT_STATE_CONFIGURED: /* ignore */ return BLT_SUCCESS; case BLT_ALSA_OUTPUT_STATE_PREPARED: /* drain samples buffered by the driver (wait until they are played) */ ATX_LOG_FINER("snd_pcm_drain"); snd_pcm_drain(self->device_handle); break; } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Activate +---------------------------------------------------------------------*/ BLT_METHOD AlsaOutput_Activate(BLT_MediaNode* _self, BLT_Stream* stream) { AlsaOutput* self = ATX_SELF_EX(AlsaOutput, BLT_BaseMediaNode, BLT_MediaNode); BLT_COMPILER_UNUSED(stream); ATX_LOG_FINER("activating output"); /* open the device */ AlsaOutput_Open(self); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | SilenceRemover_AcceptPacket +---------------------------------------------------------------------*/ static void SilenceRemover_AcceptPacket(SilenceRemover* self, BLT_MediaPacket* packet) { BLT_Result result; ATX_LOG_FINER("SilenceRemover: accepting packet"); /* first, use any pending packet */ SilenceRemover_AcceptPending(self); /* add the packet to the output list */ result = ATX_List_AddData(self->output.packets, packet); if (ATX_SUCCEEDED(result)) { BLT_MediaPacket_AddReference(packet); } }
/*---------------------------------------------------------------------- | DcfParser_Deactivate +---------------------------------------------------------------------*/ BLT_METHOD DcfParser_Deactivate(BLT_MediaNode* _self) { DcfParser* self = ATX_SELF_EX(DcfParser, BLT_BaseMediaNode, BLT_MediaNode); ATX_LOG_FINER("DcfParser::Deactivate"); /* release the stream */ ATX_RELEASE_OBJECT(self->output.stream); /* call the base class method */ BLT_BaseMediaNode_Deactivate(_self); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Unprepare +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Unprepare(AlsaOutput* self) { ATX_LOG_FINER("unpreparing output"); switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: case BLT_ALSA_OUTPUT_STATE_OPEN: case BLT_ALSA_OUTPUT_STATE_CONFIGURED: /* ignore */ break; case BLT_ALSA_OUTPUT_STATE_PREPARED: /* drain any pending samples */ ATX_LOG_FINER("snd_pcm_drain"); snd_pcm_drain(self->device_handle); /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_CONFIGURED); break; } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | OsxAudioUnitsOutput_Deactivate +---------------------------------------------------------------------*/ BLT_METHOD OsxAudioUnitsOutput_Deactivate(BLT_MediaNode* _self) { OsxAudioUnitsOutput* self = ATX_SELF_EX(OsxAudioUnitsOutput, BLT_BaseMediaNode, BLT_MediaNode); ComponentResult result; ATX_LOG_FINER("OsxAudioUnitsOutput::Deactivate"); /* un-initialize the device */ if (self->audio_unit) { result = AudioUnitUninitialize(self->audio_unit); if (result != noErr) { ATX_LOG_WARNING_1("AudioUnitUninitialize failed (%d)", (int)result); return BLT_FAILURE; } } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Resume +---------------------------------------------------------------------*/ BLT_METHOD AlsaOutput_Resume(BLT_MediaNode* _self) { AlsaOutput* self = ATX_SELF_EX(AlsaOutput, BLT_BaseMediaNode, BLT_MediaNode); ATX_LOG_FINER("resuming output"); /* pause the device */ switch (self->state) { case BLT_ALSA_OUTPUT_STATE_PREPARED: snd_pcm_pause(self->device_handle, 0); break; default: /* ignore */ break; } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | WaveFormatter_UpdateWavHeader +---------------------------------------------------------------------*/ static BLT_Result WaveFormatter_UpdateWavHeader(WaveFormatter* self) { BLT_Result result; unsigned char buffer[4]; ATX_LOG_FINER_1("WaveFormatter::UpdateWavHeader - size = %d", self->input.size); result = ATX_OutputStream_Seek(self->output.stream, 4); if (BLT_FAILED(result)) return result; ATX_BytesFromInt32Le(buffer, (ATX_Size)self->input.size + 8+16+12); ATX_OutputStream_Write(self->output.stream, buffer, 4, NULL); result = ATX_OutputStream_Seek(self->output.stream, 40); if (BLT_FAILED(result)) return result; ATX_BytesFromInt32Le(buffer, (ATX_Size)self->input.size); ATX_OutputStream_Write(self->output.stream, buffer, 4, NULL); ATX_LOG_FINER("WaveFormatter::UpdateWavHeader - updated"); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | OsxAudioUnitsOutput_QueuePacket +---------------------------------------------------------------------*/ static BLT_Result OsxAudioUnitsOutput_QueuePacket(OsxAudioUnitsOutput* self, BLT_MediaPacket* packet) { BLT_Result result = BLT_SUCCESS; unsigned int watchdog = BLT_OSX_AUDIO_UNITS_OUTPUT_MAX_QUEUE_WAIT_COUNT; ATX_LOG_FINER("queuing packet"); /* lock the queue */ pthread_mutex_lock(&self->lock); /* wait for some space in the queue */ while (ATX_List_GetItemCount(self->packet_queue) >= self->max_packets_in_queue) { pthread_mutex_unlock(&self->lock); usleep(BLT_OSX_AUDIO_UNITS_OUTPUT_SLEEP_INTERVAL); pthread_mutex_lock(&self->lock); if (--watchdog == 0) { ATX_LOG_WARNING("*** the watchdog bit us ***"); goto end; } } /* add the packet to the queue */ ATX_List_AddData(self->packet_queue, packet); ATX_LOG_FINER_1("packet queued, %d in queue", ATX_List_GetItemCount(self->packet_queue)); /* keep a reference to the packet */ BLT_MediaPacket_AddReference(packet); end: /* unlock the queue */ pthread_mutex_unlock(&self->lock); return result; }
/*---------------------------------------------------------------------- | OsxAudioUnitsOutput_Activate +---------------------------------------------------------------------*/ BLT_METHOD OsxAudioUnitsOutput_Activate(BLT_MediaNode* _self, BLT_Stream* stream) { OsxAudioUnitsOutput* self = ATX_SELF_EX(OsxAudioUnitsOutput, BLT_BaseMediaNode, BLT_MediaNode); ComponentResult result; AURenderCallbackStruct callback; BLT_COMPILER_UNUSED(stream); ATX_LOG_FINER("start"); /* select the device */ if (self->audio_device_id) { result = AudioUnitSetProperty(self->audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &self->audio_device_id, sizeof(self->audio_device_id)); if (result != noErr) { ATX_LOG_WARNING_1("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice) failed (%d)", (int)result); } } /* initialize the output */ if (self->audio_unit) { result = AudioUnitInitialize(self->audio_unit); if (result != noErr) { ATX_LOG_WARNING_1("AudioUnitInitialize failed (%d)", (int)result); return BLT_FAILURE; } } /* set some default audio format */ { AudioStreamBasicDescription audio_desc; ATX_SetMemory(&audio_desc, 0, sizeof(audio_desc)); /* setup the audio description */ audio_desc.mFormatID = kAudioFormatLinearPCM; audio_desc.mFormatFlags = kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; audio_desc.mFramesPerPacket = 1; audio_desc.mSampleRate = 44100; audio_desc.mChannelsPerFrame = 2; audio_desc.mBitsPerChannel = 16; audio_desc.mBytesPerFrame = (audio_desc.mBitsPerChannel * audio_desc.mChannelsPerFrame) / 8; audio_desc.mBytesPerPacket = audio_desc.mBytesPerFrame * audio_desc.mFramesPerPacket; audio_desc.mReserved = 0; result = AudioUnitSetProperty(self->audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &audio_desc, sizeof(audio_desc)); if (result != noErr) { ATX_LOG_WARNING_1("AudioUnitSetProperty failed (%d)", (int)result); return BLT_FAILURE; } } /* check for downmix based on the number of supported channels */ OsxAudioUnitsOutput_CheckDownmix(self); /* setup the callback */ callback.inputProc = OsxAudioUnitsOutput_RenderCallback; callback.inputProcRefCon = _self; result = AudioUnitSetProperty(self->audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)); if (result != noErr) { ATX_LOG_SEVERE_1("AudioUnitSetProperty failed when setting callback (%d)", (int)result); return BLT_FAILURE; } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | AlsaOutput_Configure +---------------------------------------------------------------------*/ static BLT_Result AlsaOutput_Configure(AlsaOutput* self, const BLT_PcmMediaType* format) { snd_pcm_hw_params_t* hw_params; snd_pcm_sw_params_t* sw_params; unsigned int rate = format->sample_rate; unsigned int buffer_time = BLT_ALSA_DEFAULT_BUFFER_TIME; snd_pcm_uframes_t buffer_size = 0; snd_pcm_uframes_t period_size = BLT_ALSA_DEFAULT_PERIOD_SIZE; snd_pcm_format_t pcm_format_id = SND_PCM_FORMAT_UNKNOWN; int ior; BLT_Result result; switch (self->state) { case BLT_ALSA_OUTPUT_STATE_CLOSED: /* first, we need to open the device */ result = AlsaOutput_Open(self); if (BLT_FAILED(result)) return result; /* FALLTHROUGH */ case BLT_ALSA_OUTPUT_STATE_CONFIGURED: case BLT_ALSA_OUTPUT_STATE_PREPARED: /* check to see if the format has changed */ if (format->sample_rate != self->media_type.sample_rate || format->channel_count != self->media_type.channel_count || format->bits_per_sample != self->media_type.bits_per_sample) { /* new format */ /* check the format */ if (format->sample_rate == 0 || format->channel_count == 0 || format->bits_per_sample == 0) { return BLT_ERROR_INVALID_MEDIA_FORMAT; } /* unprepare (forget current settings) */ result = AlsaOutput_Unprepare(self); if (BLT_FAILED(result)) return result; } else { /* same format, do nothing */ return BLT_SUCCESS; } /* FALLTHROUGH */ case BLT_ALSA_OUTPUT_STATE_OPEN: /* configure the device with the new format */ ATX_LOG_FINER("configuring ALSA device"); /* copy the format */ self->media_type = *format; ATX_LOG_FINE_3("new format: sr=%d, ch=%d, bps=%d", format->sample_rate, format->channel_count, format->bits_per_sample); /* allocate a new blank configuration */ snd_pcm_hw_params_alloca_no_assert(&hw_params); snd_pcm_hw_params_any(self->device_handle, hw_params); /* use interleaved access */ ior = snd_pcm_hw_params_set_access(self->device_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); if (ior != 0) { ATX_LOG_WARNING_2("snd_pcm_hw_params_set_access failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* set the sample rate */ ior = snd_pcm_hw_params_set_rate_near(self->device_handle, hw_params, &rate, NULL); if (ior != 0) { ATX_LOG_WARNING_3("snd_pcm_hw_params_set_rate_near(%d) failed (%d:%s)", rate, ior, snd_strerror(ior)); return BLT_FAILURE; } /* set the number of channels */ ior = snd_pcm_hw_params_set_channels(self->device_handle, hw_params, format->channel_count); if (ior != 0) { ATX_LOG_WARNING_3("snd_pcm_hw_params_set_channels(%d) failed (%d:%s)", format->channel_count, ior, snd_strerror(ior)); return BLT_FAILURE; } /* set the sample format */ switch (format->sample_format) { case BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_LE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_LE"); switch (format->bits_per_sample) { case 8: pcm_format_id = SND_PCM_FORMAT_S8; break; case 16: pcm_format_id = SND_PCM_FORMAT_S16_LE; break; case 24: pcm_format_id = SND_PCM_FORMAT_S24_3LE; break; case 32: pcm_format_id = SND_PCM_FORMAT_S32_LE; break; } break; case BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_LE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_LE"); switch (format->bits_per_sample) { case 8: pcm_format_id = SND_PCM_FORMAT_U8; break; case 16: pcm_format_id = SND_PCM_FORMAT_U16_LE; break; case 24: pcm_format_id = SND_PCM_FORMAT_U24_3LE; break; case 32: pcm_format_id = SND_PCM_FORMAT_U32_LE; break; } break; case BLT_PCM_SAMPLE_FORMAT_FLOAT_LE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_FLOAT_LE"); switch (format->bits_per_sample) { case 32: pcm_format_id = SND_PCM_FORMAT_FLOAT_LE; break; } break; case BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_BE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_SIGNED_INT_BE"); switch (format->bits_per_sample) { case 8: pcm_format_id = SND_PCM_FORMAT_S8; break; case 16: pcm_format_id = SND_PCM_FORMAT_S16_BE; break; case 24: pcm_format_id = SND_PCM_FORMAT_S24_3BE; break; case 32: pcm_format_id = SND_PCM_FORMAT_S32_BE; break; } break; case BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_BE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_UNSIGNED_INT_BE"); switch (format->bits_per_sample) { case 8: pcm_format_id = SND_PCM_FORMAT_U8; break; case 16: pcm_format_id = SND_PCM_FORMAT_U16_BE; break; case 24: pcm_format_id = SND_PCM_FORMAT_U24_3BE; break; case 32: pcm_format_id = SND_PCM_FORMAT_U32_BE; break; } break; case BLT_PCM_SAMPLE_FORMAT_FLOAT_BE: ATX_LOG_FINE("sample format is BLT_PCM_SAMPLE_FORMAT_FLOAT_LE"); switch (format->bits_per_sample) { case 32: pcm_format_id = SND_PCM_FORMAT_FLOAT_BE; break; } break; } if (pcm_format_id == SND_PCM_FORMAT_UNKNOWN) { return BLT_ERROR_INVALID_MEDIA_TYPE; } ior = snd_pcm_hw_params_set_format(self->device_handle, hw_params, pcm_format_id); if (ior != 0) { ATX_LOG_WARNING_2("snd_pcm_hw_params_set_format() failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* set the period size */ ior = snd_pcm_hw_params_set_period_size_near(self->device_handle, hw_params, &period_size, NULL); if (ior != 0) { ATX_LOG_WARNING_2("snd_pcm_hw_params_set_period_size_near() failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* set the buffer time (duration) */ ior = snd_pcm_hw_params_set_buffer_time_near(self->device_handle, hw_params, &buffer_time, NULL); if (ior != 0) { ATX_LOG_WARNING_2("snd_pcm_hw_params_set_buffer_time_near() failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* get the actual buffer size */ snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size); /* activate this configuration */ ior = snd_pcm_hw_params(self->device_handle, hw_params); if (ior != 0) { ATX_LOG_WARNING_2("snd_pcm_hw_params() failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* configure the software parameters */ snd_pcm_sw_params_alloca_no_assert(&sw_params); snd_pcm_sw_params_current(self->device_handle, sw_params); /* set the start threshold to 1/2 the buffer size */ snd_pcm_sw_params_set_start_threshold(self->device_handle, sw_params, buffer_size/2); /* set the buffer alignment */ /* NOTE: this call is now obsolete */ /* snd_pcm_sw_params_set_xfer_align(self->device_handle, sw_params, 1); */ /* activate the sofware parameters */ ior = snd_pcm_sw_params(self->device_handle, sw_params); if (ior != 0) { ATX_LOG_SEVERE_2("snd_pcm_sw_params() failed (%d:%s)", ior, snd_strerror(ior)); return BLT_FAILURE; } /* print status info */ { snd_pcm_uframes_t val; ATX_LOG_FINER_1("sample type = %x", pcm_format_id); if (rate != format->sample_rate) { ATX_LOG_FINER_1("actual sample = %d", rate); } ATX_LOG_FINER_1("actual buffer time = %d", (int)buffer_time); ATX_LOG_FINER_1("buffer size = %d", (int)buffer_size); snd_pcm_sw_params_get_start_threshold(sw_params, &val); ATX_LOG_FINER_1("start threshold = %d", (int)val); snd_pcm_sw_params_get_stop_threshold(sw_params, &val); ATX_LOG_FINER_1("stop threshold = %d", (int)val); snd_pcm_hw_params_get_period_size(hw_params, &val, NULL); ATX_LOG_FINER_1("period size = %d", (int)val); } break; } /* update the state */ AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_CONFIGURED); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | BLT_DecoderServer::Run +---------------------------------------------------------------------*/ void BLT_DecoderServer::Run() { BLT_Result result; ATX_LOG_INFO("running"); // create the decoder result = BLT_Decoder_Create(&m_Decoder); if (BLT_FAILED(result)) { m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage( BLT_DecoderServer::DecoderEvent::EVENT_TYPE_INIT_ERROR, result, "error from BLT_Decoder_Create")); WaitForTerminateMessage(); return; } // register as the event handler BLT_Decoder_SetEventListener(m_Decoder, &ATX_BASE(&m_EventListener, BLT_EventListener)); // listen to core property changes { ATX_Properties* properties; BLT_Decoder_GetProperties(m_Decoder, &properties); ATX_Properties_AddListener(properties, NULL, &ATX_BASE(&m_CorePropertyListener, ATX_PropertyListener), NULL); } // listen to stream property changes { ATX_Properties* properties; BLT_Decoder_GetStreamProperties(m_Decoder, &properties); ATX_Properties_AddListener(properties, NULL, &ATX_BASE(&m_StreamPropertyListener, ATX_PropertyListener), NULL); } // register builtins result = BLT_Decoder_RegisterBuiltins(m_Decoder); if (BLT_FAILED(result)) { m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage( BLT_DecoderServer::DecoderEvent::EVENT_TYPE_INIT_ERROR, result, "error from BLT_Decoder_RegisterBuiltins")); WaitForTerminateMessage(); return; } // set default output, default type result = BLT_Decoder_SetOutput(m_Decoder, BLT_DECODER_DEFAULT_OUTPUT_NAME, NULL); if (BLT_FAILED(result)) { m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage( BLT_DecoderServer::DecoderEvent::EVENT_TYPE_INIT_ERROR, result, "error from BLT_Decoder_SetOutput")); //WaitForTerminateMessage(); //return; } // notify the client of the initial state m_Client->PostMessage( new BLT_DecoderClient_DecoderStateNotificationMessage(STATE_STOPPED)); // initial status BLT_Decoder_GetStatus(m_Decoder, &m_DecoderStatus); m_DecoderStatus.position.range = m_PositionUpdateRange; NotifyTimeCode(); NotifyPosition(); // initial volume float volume=0.0f; result = BLT_Decoder_GetVolume(m_Decoder, &volume); if (BLT_SUCCEEDED(result)) { m_Client->PostMessage(new BLT_DecoderClient_VolumeNotificationMessage(volume)); } SetupIsComplete(); // decoding loop do { do { result = m_MessageQueue->PumpMessage(0); // non-blocking } while (BLT_SUCCEEDED(result)); if (result != NPT_ERROR_LIST_EMPTY) { break; } if (m_State == STATE_PLAYING) { result = BLT_Decoder_PumpPacketWithOptions(m_Decoder, BLT_DECODER_PUMP_OPTION_NON_BLOCKING); if (BLT_FAILED(result)) { if (result == BLT_ERROR_WOULD_BLOCK || result == BLT_ERROR_PORT_HAS_NO_DATA) { /* not fatal, just wait and try again later */ ATX_LOG_FINER("pump would block, waiting a short time"); result = m_MessageQueue->PumpMessage(BLT_PLAYER_LOOP_WAIT_DURATION); } else { ATX_LOG_FINE_1("stopped on %d", result); if (result != BLT_ERROR_EOS) { m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage( BLT_DecoderServer::DecoderEvent::EVENT_TYPE_DECODING_ERROR, result, "error from BLT_Decoder_PumpPacketWithOptions")); } SetState(STATE_EOS); result = BLT_SUCCESS; } } else { UpdateStatus(); } } else { ATX_LOG_FINE("waiting for message"); result = m_MessageQueue->PumpMessage(NPT_TIMEOUT_INFINITE); ATX_LOG_FINE("got message"); } } while (BLT_SUCCEEDED(result) || result == NPT_ERROR_TIMEOUT); ATX_LOG_FINE("received Terminate Message"); // unregister as an event listener BLT_Decoder_SetEventListener(m_Decoder, NULL); // destroy the decoder if (m_Decoder != NULL) { BLT_Decoder_Destroy(m_Decoder); } // we're done SetState(STATE_TERMINATED); }
/*---------------------------------------------------------------------- | SilenceRemoverInput_PutPacket +---------------------------------------------------------------------*/ BLT_METHOD SilenceRemoverInput_PutPacket(BLT_PacketConsumer* _self, BLT_MediaPacket* packet) { SilenceRemover* self = ATX_SELF_M(input, SilenceRemover, BLT_PacketConsumer); BLT_Flags packet_flags; BLT_Cardinal zero_head = 0; BLT_Cardinal zero_tail = 0; BLT_Offset payload_offset; BLT_Size payload_size; ATX_Result result; ATX_LOG_FINER("SilenceRemoverInput::PutPacket"); /* get the packet info */ packet_flags = BLT_MediaPacket_GetFlags(packet); payload_offset = BLT_MediaPacket_GetPayloadOffset(packet); payload_size = BLT_MediaPacket_GetPayloadSize(packet); /* scan the packet for zeros */ if (payload_size != 0) { result = ScanPacket(packet, &zero_head, &zero_tail); if (BLT_FAILED(result)) return result; if (zero_head || zero_tail) { ATX_LOG_FINER_2("SilenceRemoverInput::PutPacket zero_head=%d, zero_tail=%d", (int)zero_head, (int)zero_tail); } } /* decide how to process the packet */ if (self->state == SILENCE_REMOVER_STATE_START_OF_STREAM) { if (zero_head == payload_size) { /* packet is all silence */ if (packet_flags != 0) { /* packet has flags, don't discard it, just empty it */ ATX_LOG_FINER("SilenceRemover: emptying packet"); BLT_MediaPacket_SetPayloadSize(packet, 0); SilenceRemover_AcceptPacket(self, packet); } else { ATX_LOG_FINER("SilenceRemover: dropping packet"); } } else { /* remove silence at the start of the packet */ BLT_MediaPacket_SetPayloadOffset(packet, payload_offset+zero_head); SilenceRemover_AcceptPacket(self, packet); /* we're now in the stream unless this is also the end */ if (!(packet_flags & BLT_MEDIA_PACKET_FLAG_END_OF_STREAM)) { ATX_LOG_FINER("SilenceRemover: new state = IN_STREAM"); self->state = SILENCE_REMOVER_STATE_IN_STREAM; } } } else { /* in stream */ if (zero_head == payload_size) { /* packet is all silence */ ATX_LOG_FINER("SilenceRemover: packet is all silence"); if (packet_flags) { /* packet has flags, don't discard it, just empty it */ SilenceRemover_TrimPending(self); BLT_MediaPacket_SetPayloadSize(packet, 0); SilenceRemover_AcceptPacket(self, packet); } else { ATX_LOG_FINER("SilenceRemover: dropping packet"); } } else { /* accept the pending packet */ SilenceRemover_AcceptPending(self); if (zero_tail) { /* packet has some silence at the end */ ATX_LOG_FINER("SilenceRemover: packet has silence at end"); SilenceRemover_HoldPacket(self, packet); } else { /* packet has no silence at the end */ ATX_LOG_FINER("SilenceRemover: packet has no silence at end"); SilenceRemover_AcceptPacket(self, packet); } } if (packet_flags & BLT_MEDIA_PACKET_FLAG_END_OF_STREAM || packet_flags & BLT_MEDIA_PACKET_FLAG_START_OF_STREAM) { ATX_LOG_FINER("SilenceRemover: new state = START_OF_STREAM"); self->state = SILENCE_REMOVER_STATE_START_OF_STREAM; } } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | CrossFaderInputPort_PutPacket +---------------------------------------------------------------------*/ BLT_METHOD CrossFaderInputPort_PutPacket(BLT_PacketConsumerInstance* instance, BLT_MediaPacket* packet) { CrossFader* fader = (CrossFader*)instance; BLT_PcmMediaType* media_type; ATX_Result result; ATX_LOG_FINER_1("CrossFaderInputPort::PutPacket - state = %s", fader->state == CROSS_FADER_STATE_IN_START ? "START" : fader->state == CROSS_FADER_STATE_IN_MAIN ? "MAIN" : "???"); /* get the media type */ result = BLT_MediaPacket_GetMediaType(packet, (const BLT_MediaType**)&media_type); if (BLT_FAILED(result)) return result; /* check the if media type is PCM */ if (media_type->base.id != BLT_MEDIA_TYPE_ID_AUDIO_PCM) { return BLT_ERROR_INVALID_MEDIA_TYPE; } /* check if the media type has changed */ if (media_type->sample_rate != fader->input.media_type.sample_rate || media_type->channel_count != fader->input.media_type.channel_count || media_type->bits_per_sample != fader->input.media_type.bits_per_sample || media_type->sample_format != fader->input.media_type.sample_format) { /* media type has changed */ ATX_LOG_FINER("CrossFaderInputPort::PutPacket - new media type"); CrossFader_Flush(fader); result = CrossFader_SetupInput(fader, media_type); if (BLT_FAILED(result)) return result; } /* decide what to do with the packet */ switch (fader->state) { case CROSS_FADER_STATE_IN_START: { unsigned int sample; BLT_Size size = BLT_MediaPacket_GetPayloadSize(packet); short* samples = (short*)BLT_MediaPacket_GetPayloadBuffer(packet); float pos = (float)fader->input.position/(float)(44100*4*10); float factor = (float)pow(10.0f, -(30.0f-pos*30.0f)/20.0f); ATX_LOG_FINDER_1("CrossFaderInputPort::PutPacket - factor = %f", factor); for (sample = 0; sample < size/2; sample++) { *samples = (short)(((float)*samples)*factor); samples++; } fader->input.position += size; if (fader->input.position >= 44100*4*10) { fader->input.position = 0; fader->state = CROSS_FADER_STATE_IN_MAIN; } } CrossFader_BufferPacket(fader, packet); break; case CROSS_FADER_STATE_IN_MAIN: CrossFader_BufferPacket(fader, packet); break; } return BLT_SUCCESS; }