/*---------------------------------------------------------------------- | CrossFader_PromotePacket +---------------------------------------------------------------------*/ static BLT_Result CrossFader_PromotePacket(CrossFader* fader, BLT_Size size) { BLT_MediaPacket* packet; unsigned char* payload; BLT_Result result; ATX_LOG_FINER_1("CrossFader::PromotePacket - size = %d", size); /* create a packet */ result = BLT_Core_CreateMediaPacket(&fader->base.core, size, (const BLT_MediaType*)&fader->input.media_type, &packet); if (BLT_FAILED(result)) return result; /* get the addr of the buffer */ payload = BLT_MediaPacket_GetPayloadBuffer(packet); /* read the data from the input ring buffer */ result = ATX_RingBuffer_Read(fader->input.buffer, payload, size); if (BLT_FAILED(result)) { BLT_MediaPacket_Release(packet); return result; } /* update the size of the packet */ BLT_MediaPacket_SetPayloadSize(packet, size); /* make the packet ready for the output */ ATX_List_AddData(fader->output.packets, packet); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | CrossFader_BufferPacket +---------------------------------------------------------------------*/ static BLT_Result CrossFader_BufferPacket(CrossFader* fader, BLT_MediaPacket* packet) { BLT_Size space; BLT_Size payload_size; BLT_Any payload_buffer; /* quick check */ if (fader->input.buffer == NULL) return BLT_SUCCESS; /* see how much data we have */ payload_size = BLT_MediaPacket_GetPayloadSize(packet); payload_buffer = BLT_MediaPacket_GetPayloadBuffer(packet); /* shortcut */ if (payload_size == 0) return BLT_SUCCESS; /* ensure that we have enough space in the buffer */ space = ATX_RingBuffer_GetSpace(fader->input.buffer); if (space < payload_size) { /* not enough space */ BLT_Size available = ATX_RingBuffer_GetAvailable(fader->input.buffer); if (available >= payload_size) { CrossFader_PromotePacket(fader, payload_size); } else { CrossFader_PromotePacket(fader, available); } space = ATX_RingBuffer_GetSpace(fader->input.buffer); if (space < payload_size) { /* we can't handle it */ return BLT_FAILURE; } } /* copy the data to the input buffer */ ATX_LOG_FINER_1("CrossFader::BufferPacket - buffering %d", payload_size); ATX_RingBuffer_Write(fader->input.buffer, payload_buffer, payload_size); 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; }
/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | OsxAudioUnitsOutput_RenderCallback +---------------------------------------------------------------------*/ static OSStatus OsxAudioUnitsOutput_RenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) { OsxAudioUnitsOutput* self = (OsxAudioUnitsOutput*)inRefCon; ATX_ListItem* item; unsigned int requested; unsigned char* out; ATX_Boolean timestamp_measured = ATX_FALSE; BLT_COMPILER_UNUSED(ioActionFlags); BLT_COMPILER_UNUSED(inTimeStamp); BLT_COMPILER_UNUSED(inBusNumber); BLT_COMPILER_UNUSED(inNumberFrames); /* sanity check on the parameters */ if (ioData == NULL || ioData->mNumberBuffers == 0) return 0; /* in case we have a strange request with more than one buffer, just return silence */ if (ioData->mNumberBuffers != 1) { unsigned int i; ATX_LOG_FINEST_1("strange request with %d buffers", (int)ioData->mNumberBuffers); for (i=0; i<ioData->mNumberBuffers; i++) { ATX_SetMemory(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize); } return 0; } /* init local variables */ requested = ioData->mBuffers[0].mDataByteSize; out = (unsigned char*)ioData->mBuffers[0].mData; ATX_LOG_FINEST_2("request for %d bytes, %d frames", (int)requested, (int)inNumberFrames); /* lock the packet queue */ pthread_mutex_lock(&self->lock); /* return now if we're paused */ //if (self->paused) goto end; /* abort early if we have no packets */ if (ATX_List_GetItemCount(self->packet_queue) == 0) goto end; /* fill as much as we can */ while (requested && (item = ATX_List_GetFirstItem(self->packet_queue))) { BLT_MediaPacket* packet = ATX_ListItem_GetData(item); const BLT_PcmMediaType* media_type; BLT_Size payload_size; BLT_Size chunk_size; BLT_TimeStamp chunk_duration; BLT_TimeStamp packet_ts; unsigned int bytes_per_frame; unsigned int sample_rate; /* get the packet info */ BLT_MediaPacket_GetMediaType(packet, (const BLT_MediaType**)&media_type); packet_ts = BLT_MediaPacket_GetTimeStamp(packet); bytes_per_frame = media_type->channel_count*media_type->bits_per_sample/8; sample_rate = media_type->sample_rate; /* record the timestamp if we have not already done so */ if (!timestamp_measured) { self->media_time_snapshot.rendered_packet_ts = packet_ts; self->media_time_snapshot.rendered_host_time = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); BLT_TimeStamp_Set(self->media_time_snapshot.rendered_duration, 0, 0); timestamp_measured = ATX_TRUE; ATX_LOG_FINEST_2("rendered TS: packet ts=%lld, host ts=%lld", BLT_TimeStamp_ToNanos(packet_ts), self->media_time_snapshot.rendered_host_time); } /* compute how much to copy from this packet */ payload_size = BLT_MediaPacket_GetPayloadSize(packet); if (payload_size <= requested) { /* copy the entire payload and remove the packet from the queue */ chunk_size = payload_size; ATX_CopyMemory(out, BLT_MediaPacket_GetPayloadBuffer(packet), chunk_size); ATX_List_RemoveItem(self->packet_queue, item); packet = NULL; media_type = NULL; ATX_LOG_FINER_1("media packet fully consumed, %d left in queue", ATX_List_GetItemCount(self->packet_queue)); } else { /* only copy a portion of the payload */ chunk_size = requested; ATX_CopyMemory(out, BLT_MediaPacket_GetPayloadBuffer(packet), chunk_size); } /* update the counters */ requested -= chunk_size; out += chunk_size; /* update the media time snapshot */ if (bytes_per_frame) { unsigned int frames_in_chunk = chunk_size/bytes_per_frame; chunk_duration = BLT_TimeStamp_FromSamples(frames_in_chunk, sample_rate); } else { BLT_TimeStamp_Set(chunk_duration, 0, 0); } self->media_time_snapshot.rendered_duration = BLT_TimeStamp_Add(self->media_time_snapshot.rendered_duration, chunk_duration); /* update the packet unless we're done with it */ if (packet) { /* update the packet offset and timestamp */ BLT_MediaPacket_SetPayloadOffset(packet, BLT_MediaPacket_GetPayloadOffset(packet)+chunk_size); BLT_MediaPacket_SetTimeStamp(packet, BLT_TimeStamp_Add(packet_ts, chunk_duration)); } } end: /* fill whatever is left with silence */ if (requested) { ATX_LOG_FINEST_1("filling with %d bytes of silence", requested); ATX_SetMemory(out, 0, requested); } pthread_mutex_unlock(&self->lock); return 0; }