static const struct audio_format * route_filter_open(struct filter *_filter, struct audio_format *audio_format, G_GNUC_UNUSED GError **error_r) { struct route_filter *filter = (struct route_filter *)_filter; // Copy the input format for later reference filter->input_format = *audio_format; filter->input_frame_size = audio_format_frame_size(&filter->input_format); // Decide on an output format which has enough channels, // and is otherwise identical filter->output_format = *audio_format; filter->output_format.channels = filter->min_output_channels; // Precalculate this simple value, to speed up allocation later filter->output_frame_size = audio_format_frame_size(&filter->output_format); // This buffer grows as needed pcm_buffer_init(&filter->output_buffer); return &filter->output_format; }
/** * Sends a chunk of silence to the audio outputs. This is called when * there is not enough decoded data in the pipe yet, to prevent * underruns in the hardware buffers. * * The player lock is not held. */ static bool player_send_silence(struct player *player) { assert(audio_format_defined(&player->play_audio_format)); struct music_chunk *chunk = music_buffer_allocate(player_buffer); if (chunk == NULL) { g_warning("Failed to allocate silence buffer"); return false; } #ifndef NDEBUG chunk->audio_format = player->play_audio_format; #endif size_t frame_size = audio_format_frame_size(&player->play_audio_format); /* this formula ensures that we don't send partial frames */ unsigned num_frames = sizeof(chunk->data) / frame_size; chunk->times = -1.0; /* undefined time stamp */ chunk->length = num_frames * frame_size; memset(chunk->data, 0, chunk->length); if (!audio_output_all_play(chunk)) { music_buffer_return(player_buffer, chunk); return false; } return true; }
/** * This function attempts to call decoder_initialized() in case there * was no STREAMINFO block. This is allowed for nonseekable streams, * where the server sends us only a part of the file, without * providing the STREAMINFO block from the beginning of the file * (e.g. when seeking with SqueezeBox Server). */ static bool flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header) { if (data->unsupported) return false; GError *error = NULL; if (!audio_format_init_checked(&data->audio_format, header->sample_rate, flac_sample_format(header->bits_per_sample), header->channels, &error)) { g_warning("%s", error->message); g_error_free(error); data->unsupported = true; return false; } data->frame_size = audio_format_frame_size(&data->audio_format); decoder_initialized(data->decoder, &data->audio_format, data->input_stream->seekable, (float)data->total_frames / (float)data->audio_format.sample_rate); data->initialized = true; return true; }
static void flac_got_stream_info(struct flac_data *data, const FLAC__StreamMetadata_StreamInfo *stream_info) { if (data->initialized || data->unsupported) return; GError *error = NULL; if (!audio_format_init_checked(&data->audio_format, stream_info->sample_rate, flac_sample_format(stream_info->bits_per_sample), stream_info->channels, &error)) { g_warning("%s", error->message); g_error_free(error); data->unsupported = true; return; } data->frame_size = audio_format_frame_size(&data->audio_format); if (data->total_frames == 0) data->total_frames = stream_info->total_samples; data->initialized = true; }
static bool twolame_encoder_write(struct encoder *_encoder, const void *data, size_t length, G_GNUC_UNUSED GError **error) { struct twolame_encoder *encoder = (struct twolame_encoder *)_encoder; unsigned num_frames; const int16_t *src = (const int16_t*)data; int bytes_out; assert(encoder->buffer_length == 0); num_frames = length / audio_format_frame_size(&encoder->audio_format); bytes_out = twolame_encode_buffer_interleaved(encoder->options, src, num_frames, encoder->buffer, sizeof(encoder->buffer)); if (bytes_out < 0) { g_set_error(error, twolame_encoder_quark(), 0, "twolame encoder failed"); return false; } encoder->buffer_length = (size_t)bytes_out; return true; }
static void sndfile_stream_decode(struct decoder *decoder, struct input_stream *is) { GError *error = NULL; SNDFILE *sf; SF_INFO info; struct audio_format audio_format; size_t frame_size; sf_count_t read_frames, num_frames; int buffer[4096]; enum decoder_command cmd; info.format = 0; sf = sf_open_virtual(&vio, SFM_READ, &info, is); if (sf == NULL) { g_warning("sf_open_virtual() failed"); return; } /* for now, always read 32 bit samples. Later, we could lower MPD's CPU usage by reading 16 bit samples with sf_readf_short() on low-quality source files. */ if (!audio_format_init_checked(&audio_format, info.samplerate, SAMPLE_FORMAT_S32, info.channels, &error)) { g_warning("%s", error->message); g_error_free(error); return; } decoder_initialized(decoder, &audio_format, info.seekable, frame_to_time(info.frames, &audio_format)); frame_size = audio_format_frame_size(&audio_format); read_frames = sizeof(buffer) / frame_size; do { num_frames = sf_readf_int(sf, buffer, read_frames); if (num_frames <= 0) break; cmd = decoder_data(decoder, is, buffer, num_frames * frame_size, 0); if (cmd == DECODE_COMMAND_SEEK) { sf_count_t c = time_to_frame(decoder_seek_where(decoder), &audio_format); c = sf_seek(sf, c, SEEK_SET); if (c < 0) decoder_seek_error(decoder); else decoder_command_finished(decoder); cmd = DECODE_COMMAND_NONE; } } while (cmd == DECODE_COMMAND_NONE); sf_close(sf); }
static bool ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) { GError *error = NULL; assert(ao != NULL); assert(ao->filter != NULL); if (chunk->tag != NULL) { g_mutex_unlock(ao->mutex); ao_plugin_send_tag(ao->plugin, ao->data, chunk->tag); g_mutex_lock(ao->mutex); } size_t size; const char *data = ao_filter_chunk(ao, chunk, &size); if (data == NULL) { ao_close(ao, false); /* don't automatically reopen this device for 10 seconds */ ao->fail_timer = g_timer_new(); return false; } while (size > 0 && ao->command == AO_COMMAND_NONE) { size_t nbytes; if (!ao_wait(ao)) break; g_mutex_unlock(ao->mutex); nbytes = ao_plugin_play(ao->plugin, ao->data, data, size, &error); g_mutex_lock(ao->mutex); if (nbytes == 0) { /* play()==0 means failure */ g_warning("\"%s\" [%s] failed to play: %s", ao->name, ao->plugin->name, error->message); g_error_free(error); ao_close(ao, false); /* don't automatically reopen this device for 10 seconds */ assert(ao->fail_timer == NULL); ao->fail_timer = g_timer_new(); return false; } assert(nbytes <= size); assert(nbytes % audio_format_frame_size(&ao->out_audio_format) == 0); data += nbytes; size -= nbytes; } return true; }
static bool winmm_output_open(void *data, struct audio_format *audio_format, GError **error_r) { struct winmm_output *wo = data; wo->event = CreateEvent(NULL, false, false, NULL); if (wo->event == NULL) { g_set_error(error_r, winmm_output_quark(), 0, "CreateEvent() failed"); return false; } switch (audio_format->format) { case SAMPLE_FORMAT_S8: case SAMPLE_FORMAT_S16: break; case SAMPLE_FORMAT_S24: case SAMPLE_FORMAT_S24_P32: case SAMPLE_FORMAT_S32: case SAMPLE_FORMAT_UNDEFINED: /* we havn't tested formats other than S16 */ audio_format->format = SAMPLE_FORMAT_S16; break; } if (audio_format->channels > 2) /* same here: more than stereo was not tested */ audio_format->channels = 2; WAVEFORMATEX format; format.wFormatTag = WAVE_FORMAT_PCM; format.nChannels = audio_format->channels; format.nSamplesPerSec = audio_format->sample_rate; format.nBlockAlign = audio_format_frame_size(audio_format); format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; format.wBitsPerSample = audio_format_sample_size(audio_format) * 8; format.cbSize = 0; MMRESULT result = waveOutOpen(&wo->handle, wo->device_id, &format, (DWORD_PTR)wo->event, 0, CALLBACK_EVENT); if (result != MMSYSERR_NOERROR) { CloseHandle(wo->event); g_set_error(error_r, winmm_output_quark(), result, "waveOutOpen() failed"); return false; } for (unsigned i = 0; i < G_N_ELEMENTS(wo->buffers); ++i) { pcm_buffer_init(&wo->buffers[i].buffer); memset(&wo->buffers[i].hdr, 0, sizeof(wo->buffers[i].hdr)); } wo->next_buffer = 0; return true; }
Timer *timer_new(const struct audio_format *af) { Timer *timer = g_new(Timer, 1); timer->time = 0; timer->started = 0; timer->rate = af->sample_rate * audio_format_frame_size(af); return timer; }
static bool flac_encoder_write(struct encoder *_encoder, const void *data, size_t length, G_GNUC_UNUSED GError **error) { struct flac_encoder *encoder = (struct flac_encoder *)_encoder; unsigned num_frames, num_samples; void *exbuffer; const void *buffer = NULL; /* format conversion */ num_frames = length / audio_format_frame_size(&encoder->audio_format); num_samples = num_frames * encoder->audio_format.channels; switch (encoder->audio_format.format) { case SAMPLE_FORMAT_S8: exbuffer = pcm_buffer_get(&encoder->expand_buffer, length*4); pcm8_to_flac(exbuffer, data, num_samples); buffer = exbuffer; break; case SAMPLE_FORMAT_S16: exbuffer = pcm_buffer_get(&encoder->expand_buffer, length*2); pcm16_to_flac(exbuffer, data, num_samples); buffer = exbuffer; break; case SAMPLE_FORMAT_S24_P32: case SAMPLE_FORMAT_S32: /* nothing need to be done; format is the same for both mpd and libFLAC */ buffer = data; break; } /* feed samples to encoder */ if (!FLAC__stream_encoder_process_interleaved(encoder->fse, buffer, num_frames)) { g_set_error(error, flac_encoder_quark(), 0, "flac encoder process failed"); return false; } return true; }
static bool lame_encoder_write(struct encoder *_encoder, const void *data, size_t length, G_GNUC_UNUSED GError **error) { struct lame_encoder *encoder = (struct lame_encoder *)_encoder; unsigned num_frames; float *left, *right; const int16_t *src = (const int16_t*)data; unsigned int i; int bytes_out; assert(encoder->buffer_length == 0); num_frames = length / audio_format_frame_size(&encoder->audio_format); left = g_malloc(sizeof(left[0]) * num_frames); right = g_malloc(sizeof(right[0]) * num_frames); /* this is for only 16-bit audio */ for (i = 0; i < num_frames; i++) { left[i] = *src++; right[i] = *src++; } bytes_out = lame_encode_buffer_float(encoder->gfp, left, right, num_frames, encoder->buffer, sizeof(encoder->buffer)); g_free(left); g_free(right); if (bytes_out < 0) { g_set_error(error, lame_encoder_quark(), 0, "lame encoder failed"); return false; } encoder->buffer_length = (size_t)bytes_out; return true; }
static bool vorbis_encoder_write(struct encoder *_encoder, const void *data, size_t length, G_GNUC_UNUSED GError **error) { struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; unsigned num_frames; num_frames = length / audio_format_frame_size(&encoder->audio_format); /* this is for only 16-bit audio */ pcm16_to_vorbis_buffer(vorbis_analysis_buffer(&encoder->vd, num_frames), (const int16_t *)data, num_frames, encoder->audio_format.channels); vorbis_analysis_wrote(&encoder->vd, num_frames); vorbis_encoder_blockout(encoder); return true; }
static const char * ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk, struct filter *replay_gain_filter, unsigned *replay_gain_serial_p, size_t *length_r) { assert(chunk != NULL); assert(!music_chunk_is_empty(chunk)); assert(music_chunk_check_format(chunk, &ao->in_audio_format)); const char *data = chunk->data; size_t length = chunk->length; (void)ao; assert(length % audio_format_frame_size(&ao->in_audio_format) == 0); if (length > 0 && replay_gain_filter != NULL) { if (chunk->replay_gain_serial != *replay_gain_serial_p) { replay_gain_filter_set_info(replay_gain_filter, chunk->replay_gain_serial != 0 ? &chunk->replay_gain_info : NULL); *replay_gain_serial_p = chunk->replay_gain_serial; } GError *error = NULL; data = filter_filter(replay_gain_filter, data, length, &length, &error); if (data == NULL) { g_warning("\"%s\" [%s] failed to filter: %s", ao->name, ao->plugin->name, error->message); g_error_free(error); return NULL; } } *length_r = length; return data; }
static bool osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error) { struct osx_output *od = (struct osx_output *)ao; AudioStreamBasicDescription stream_description; stream_description.mSampleRate = audio_format->sample_rate; stream_description.mFormatID = kAudioFormatLinearPCM; stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; switch (audio_format->format) { case SAMPLE_FORMAT_S8: stream_description.mBitsPerChannel = 8; break; case SAMPLE_FORMAT_S16: stream_description.mBitsPerChannel = 16; break; case SAMPLE_FORMAT_S32: stream_description.mBitsPerChannel = 32; break; default: audio_format->format = SAMPLE_FORMAT_S32; stream_description.mBitsPerChannel = 32; break; } #if G_BYTE_ORDER == G_BIG_ENDIAN stream_description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; #endif stream_description.mBytesPerPacket = audio_format_frame_size(audio_format); stream_description.mFramesPerPacket = 1; stream_description.mBytesPerFrame = stream_description.mBytesPerPacket; stream_description.mChannelsPerFrame = audio_format->channels; ComponentResult result = AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &stream_description, sizeof(stream_description)); if (result != noErr) { g_set_error(error, osx_output_quark(), result, "Unable to set format on OS X device"); return false; } OSStatus status = AudioUnitInitialize(od->au); if (status != noErr) { g_set_error(error, osx_output_quark(), status, "Unable to initialize OS X audio unit: %s", GetMacOSStatusCommentString(status)); return false; } /* create a buffer of 1s */ od->buffer = fifo_buffer_new(audio_format->sample_rate * audio_format_frame_size(audio_format)); status = AudioOutputUnitStart(od->au); if (status != 0) { AudioUnitUninitialize(od->au); g_set_error(error, osx_output_quark(), status, "unable to start audio output: %s", GetMacOSStatusCommentString(status)); return false; } return true; }
static bool ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) { const char *data = chunk->data; size_t size = chunk->length; GError *error = NULL; assert(ao != NULL); assert(ao->filter != NULL); assert(!music_chunk_is_empty(chunk)); assert(music_chunk_check_format(chunk, &ao->in_audio_format)); assert(size % audio_format_frame_size(&ao->in_audio_format) == 0); if (chunk->tag != NULL) { g_mutex_unlock(ao->mutex); ao_plugin_send_tag(ao->plugin, ao->data, chunk->tag); g_mutex_lock(ao->mutex); } /* update replay gain */ if (ao->replay_gain_filter != NULL && chunk->replay_gain_serial != ao->replay_gain_serial) { replay_gain_filter_set_info(ao->replay_gain_filter, chunk->replay_gain_serial != 0 ? &chunk->replay_gain_info : NULL); ao->replay_gain_serial = chunk->replay_gain_serial; } if (size == 0) return true; data = filter_filter(ao->filter, data, size, &size, &error); if (data == NULL) { g_warning("\"%s\" [%s] failed to filter: %s", ao->name, ao->plugin->name, error->message); g_error_free(error); ao_close(ao, false); /* don't automatically reopen this device for 10 seconds */ ao->fail_timer = g_timer_new(); return false; } while (size > 0 && ao->command == AO_COMMAND_NONE) { size_t nbytes; g_mutex_unlock(ao->mutex); nbytes = ao_plugin_play(ao->plugin, ao->data, data, size, &error); g_mutex_lock(ao->mutex); if (nbytes == 0) { /* play()==0 means failure */ g_warning("\"%s\" [%s] failed to play: %s", ao->name, ao->plugin->name, error->message); g_error_free(error); ao_close(ao, false); /* don't automatically reopen this device for 10 seconds */ ao->fail_timer = g_timer_new(); return false; } assert(nbytes <= size); assert(nbytes % audio_format_frame_size(&ao->out_audio_format) == 0); data += nbytes; size -= nbytes; } return true; }
/* * This does the main decoding thing. * Requires an already opened WavpackContext. */ static void wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek) { GError *error = NULL; bool is_float; enum sample_format sample_format; struct audio_format audio_format; format_samples_t format_samples; float total_time; int bytes_per_sample, output_sample_size; is_float = (WavpackGetMode(wpc) & MODE_FLOAT) != 0; sample_format = wavpack_bits_to_sample_format(is_float, WavpackGetBytesPerSample(wpc)); if (!audio_format_init_checked(&audio_format, WavpackGetSampleRate(wpc), sample_format, WavpackGetNumChannels(wpc), &error)) { g_warning("%s", error->message); g_error_free(error); return; } if (is_float) { format_samples = format_samples_float; } else { format_samples = format_samples_int; } total_time = WavpackGetNumSamples(wpc); total_time /= audio_format.sample_rate; bytes_per_sample = WavpackGetBytesPerSample(wpc); output_sample_size = audio_format_frame_size(&audio_format); /* wavpack gives us all kind of samples in a 32-bit space */ int32_t chunk[1024]; const uint32_t samples_requested = G_N_ELEMENTS(chunk) / audio_format.channels; decoder_initialized(decoder, &audio_format, can_seek, total_time); enum decoder_command cmd = decoder_get_command(decoder); while (cmd != DECODE_COMMAND_STOP) { if (cmd == DECODE_COMMAND_SEEK) { if (can_seek) { unsigned where = decoder_seek_where(decoder) * audio_format.sample_rate; if (WavpackSeekSample(wpc, where)) { decoder_command_finished(decoder); } else { decoder_seek_error(decoder); } } else { decoder_seek_error(decoder); } } uint32_t samples_got = WavpackUnpackSamples(wpc, chunk, samples_requested); if (samples_got == 0) break; int bitrate = (int)(WavpackGetInstantBitrate(wpc) / 1000 + 0.5); format_samples(bytes_per_sample, chunk, samples_got * audio_format.channels); cmd = decoder_data(decoder, NULL, chunk, samples_got * output_sample_size, bitrate); } }
enum decoder_command decoder_data(struct decoder *decoder, struct input_stream *is, const void *_data, size_t length, uint16_t kbit_rate) { struct decoder_control *dc = decoder->dc; const char *data = _data; GError *error = NULL; enum decoder_command cmd; assert(dc->state == DECODE_STATE_DECODE); assert(dc->pipe != NULL); assert(length % audio_format_frame_size(&dc->in_audio_format) == 0); decoder_lock(dc); cmd = decoder_get_virtual_command(decoder); decoder_unlock(dc); if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK || length == 0) return cmd; /* send stream tags */ if (update_stream_tag(decoder, is)) { if (decoder->decoder_tag != NULL) { /* merge with tag from decoder plugin */ struct tag *tag; tag = tag_merge(decoder->decoder_tag, decoder->stream_tag); cmd = do_send_tag(decoder, is, tag); tag_free(tag); } else /* send only the stream tag */ cmd = do_send_tag(decoder, is, decoder->stream_tag); if (cmd != DECODE_COMMAND_NONE) return cmd; } if (!audio_format_equals(&dc->in_audio_format, &dc->out_audio_format)) { data = pcm_convert(&decoder->conv_state, &dc->in_audio_format, data, length, &dc->out_audio_format, &length, &error); if (data == NULL) { /* the PCM conversion has failed - stop playback, since we have no better way to bail out */ g_warning("%s", error->message); return DECODE_COMMAND_STOP; } } while (length > 0) { struct music_chunk *chunk; char *dest; size_t nbytes; bool full; chunk = decoder_get_chunk(decoder, is); if (chunk == NULL) { assert(dc->command != DECODE_COMMAND_NONE); return dc->command; } dest = music_chunk_write(chunk, &dc->out_audio_format, decoder->timestamp - dc->song->start_ms / 1000.0, kbit_rate, &nbytes); if (dest == NULL) { /* the chunk is full, flush it */ decoder_flush_chunk(decoder); player_lock_signal(); continue; } assert(nbytes > 0); if (nbytes > length) nbytes = length; /* copy the buffer */ memcpy(dest, data, nbytes); /* expand the music pipe chunk */ full = music_chunk_expand(chunk, &dc->out_audio_format, nbytes); if (full) { /* the chunk is full, flush it */ decoder_flush_chunk(decoder); player_lock_signal(); } data += nbytes; length -= nbytes; decoder->timestamp += (double)nbytes / audio_format_time_to_size(&dc->out_audio_format); if (dc->end_ms > 0 && decoder->timestamp >= dc->end_ms / 1000.0) /* the end of this range has been reached: stop decoding */ return DECODE_COMMAND_STOP; } return DECODE_COMMAND_NONE; }
static bool osx_output_open(void *data, struct audio_format *audio_format, GError **error) { struct osx_output *od = data; ComponentDescription desc; Component comp; AURenderCallbackStruct callback; AudioStreamBasicDescription stream_description; OSStatus status; ComponentResult result; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_DefaultOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; comp = FindNextComponent(NULL, &desc); if (comp == 0) { g_set_error(error, osx_output_quark(), 0, "Error finding OS X component"); return false; } status = OpenAComponent(comp, &od->au); if (status != noErr) { g_set_error(error, osx_output_quark(), 0, "Unable to open OS X component: %s", GetMacOSStatusCommentString(status)); return false; } status = AudioUnitInitialize(od->au); if (status != noErr) { CloseComponent(od->au); g_set_error(error, osx_output_quark(), 0, "Unable to initialize OS X audio unit: %s", GetMacOSStatusCommentString(status)); return false; } callback.inputProc = osx_render; callback.inputProcRefCon = od; result = AudioUnitSetProperty(od->au, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)); if (result != noErr) { AudioUnitUninitialize(od->au); CloseComponent(od->au); g_set_error(error, osx_output_quark(), 0, "unable to set callback for OS X audio unit"); return false; } stream_description.mSampleRate = audio_format->sample_rate; stream_description.mFormatID = kAudioFormatLinearPCM; stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; #if G_BYTE_ORDER == G_BIG_ENDIAN stream_description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; #endif stream_description.mBytesPerPacket = audio_format_frame_size(audio_format); stream_description.mFramesPerPacket = 1; stream_description.mBytesPerFrame = stream_description.mBytesPerPacket; stream_description.mChannelsPerFrame = audio_format->channels; switch (audio_format->format) { case SAMPLE_FORMAT_S8: stream_description.mBitsPerChannel = 8; break; case SAMPLE_FORMAT_S16: stream_description.mBitsPerChannel = 16; break; default: audio_format->format = SAMPLE_FORMAT_S16; stream_description.mBitsPerChannel = 16; break; } result = AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &stream_description, sizeof(stream_description)); if (result != noErr) { AudioUnitUninitialize(od->au); CloseComponent(od->au); g_set_error(error, osx_output_quark(), 0, "Unable to set format on OS X device"); return false; } /* create a buffer of 1s */ od->buffer_size = (audio_format->sample_rate) * audio_format_frame_size(audio_format); od->buffer = g_realloc(od->buffer, od->buffer_size); od->pos = 0; od->len = 0; status = AudioOutputUnitStart(od->au); if (status != 0) { g_set_error(error, osx_output_quark(), 0, "unable to start audio output: %s", GetMacOSStatusCommentString(status)); return false; } return true; }
int main(int argc, char **argv) { struct audio_format audio_format; struct audio_format_string af_string; bool success; GError *error = NULL; struct filter *filter; const struct audio_format *out_audio_format; char buffer[4096]; size_t frame_size; if (argc < 3 || argc > 4) { g_printerr("Usage: run_filter CONFIG NAME [FORMAT] <IN\n"); return 1; } audio_format_init(&audio_format, 44100, SAMPLE_FORMAT_S16, 2); /* initialize GLib */ g_thread_init(NULL); g_log_set_default_handler(my_log_func, NULL); /* read configuration file (mpd.conf) */ config_global_init(); success = config_read_file(argv[1], &error); if (!success) { g_printerr("%s:", error->message); g_error_free(error); return 1; } /* parse the audio format */ if (argc > 3) { success = audio_format_parse(&audio_format, argv[3], false, &error); if (!success) { g_printerr("Failed to parse audio format: %s\n", error->message); g_error_free(error); return 1; } } /* initialize the filter */ filter = load_filter(argv[2]); if (filter == NULL) return 1; /* open the filter */ out_audio_format = filter_open(filter, &audio_format, &error); if (out_audio_format == NULL) { g_printerr("Failed to open filter: %s\n", error->message); g_error_free(error); filter_free(filter); return 1; } g_printerr("audio_format=%s\n", audio_format_to_string(out_audio_format, &af_string)); frame_size = audio_format_frame_size(&audio_format); /* play */ while (true) { ssize_t nbytes; size_t length; const void *dest; nbytes = read(0, buffer, sizeof(buffer)); if (nbytes <= 0) break; dest = filter_filter(filter, buffer, (size_t)nbytes, &length, &error); if (dest == NULL) { g_printerr("Filter failed: %s\n", error->message); filter_close(filter); filter_free(filter); return 1; } nbytes = write(1, dest, length); if (nbytes < 0) { g_printerr("Failed to write: %s\n", strerror(errno)); filter_close(filter); filter_free(filter); return 1; } } /* cleanup and exit */ filter_close(filter); filter_free(filter); config_global_finish(); return 0; }
int main(int argc, char **argv) { struct audio_output ao; struct audio_format audio_format; struct audio_format_string af_string; bool success; GError *error = NULL; char buffer[4096]; ssize_t nbytes; size_t frame_size, length = 0, play_length, consumed; if (argc < 3 || argc > 4) { g_printerr("Usage: run_output CONFIG NAME [FORMAT] <IN\n"); return 1; } audio_format_init(&audio_format, 44100, SAMPLE_FORMAT_S16, 2); g_thread_init(NULL); /* read configuration file (mpd.conf) */ config_global_init(); success = config_read_file(argv[1], &error); if (!success) { g_printerr("%s:", error->message); g_error_free(error); return 1; } /* initialize the audio output */ if (!load_audio_output(&ao, argv[2])) return 1; /* parse the audio format */ if (argc > 3) { success = audio_format_parse(&audio_format, argv[3], false, &error); if (!success) { g_printerr("Failed to parse audio format: %s\n", error->message); g_error_free(error); return 1; } } /* open the audio output */ success = ao_plugin_open(ao.plugin, ao.data, &audio_format, &error); if (!success) { g_printerr("Failed to open audio output: %s\n", error->message); g_error_free(error); return 1; } g_printerr("audio_format=%s\n", audio_format_to_string(&audio_format, &af_string)); frame_size = audio_format_frame_size(&audio_format); /* play */ while (true) { if (length < sizeof(buffer)) { nbytes = read(0, buffer + length, sizeof(buffer) - length); if (nbytes <= 0) break; length += (size_t)nbytes; } play_length = (length / frame_size) * frame_size; if (play_length > 0) { consumed = ao_plugin_play(ao.plugin, ao.data, buffer, play_length, &error); if (consumed == 0) { g_printerr("Failed to play: %s\n", error->message); g_error_free(error); return 1; } assert(consumed <= length); assert(consumed % frame_size == 0); length -= consumed; memmove(buffer, buffer + consumed, length); } } /* cleanup and exit */ ao_plugin_close(ao.plugin, ao.data); ao_plugin_finish(ao.plugin, ao.data); g_mutex_free(ao.mutex); config_global_finish(); return 0; }
/* * This does the main decoding thing. * Requires an already opened WavpackContext. */ static void wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek, struct replay_gain_info *replay_gain_info) { struct audio_format audio_format; format_samples_t format_samples; char chunk[CHUNK_SIZE]; int samples_requested, samples_got; float total_time, current_time; int bytes_per_sample, output_sample_size; int position; audio_format.sample_rate = WavpackGetSampleRate(wpc); audio_format.channels = WavpackGetReducedChannels(wpc); audio_format.bits = WavpackGetBitsPerSample(wpc); /* round bitwidth to 8-bit units */ audio_format.bits = (audio_format.bits + 7) & (~7); /* mpd handles max 24-bit samples */ if (audio_format.bits > 24) { audio_format.bits = 24; } if (!audio_format_valid(&audio_format)) { g_warning("Invalid audio format: %u:%u:%u\n", audio_format.sample_rate, audio_format.bits, audio_format.channels); return; } if ((WavpackGetMode(wpc) & MODE_FLOAT) == MODE_FLOAT) { format_samples = format_samples_float; } else { format_samples = format_samples_int; } total_time = WavpackGetNumSamples(wpc); total_time /= audio_format.sample_rate; bytes_per_sample = WavpackGetBytesPerSample(wpc); output_sample_size = audio_format_frame_size(&audio_format); /* wavpack gives us all kind of samples in a 32-bit space */ samples_requested = sizeof(chunk) / (4 * audio_format.channels); decoder_initialized(decoder, &audio_format, can_seek, total_time); position = 0; do { if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK) { if (can_seek) { int where; where = decoder_seek_where(decoder); where *= audio_format.sample_rate; if (WavpackSeekSample(wpc, where)) { position = where; decoder_command_finished(decoder); } else { decoder_seek_error(decoder); } } else { decoder_seek_error(decoder); } } if (decoder_get_command(decoder) == DECODE_COMMAND_STOP) { break; } samples_got = WavpackUnpackSamples( wpc, (int32_t *)chunk, samples_requested ); if (samples_got > 0) { int bitrate = (int)(WavpackGetInstantBitrate(wpc) / 1000 + 0.5); position += samples_got; current_time = position; current_time /= audio_format.sample_rate; format_samples( bytes_per_sample, chunk, samples_got * audio_format.channels ); decoder_data( decoder, NULL, chunk, samples_got * output_sample_size, current_time, bitrate, replay_gain_info ); } } while (samples_got > 0); }
enum decoder_command decoder_data(struct decoder *decoder, struct input_stream *is, const void *_data, size_t length, float data_time, uint16_t bitRate, struct replay_gain_info *replay_gain_info) { const char *data = _data; assert(dc.state == DECODE_STATE_DECODE); assert(length % audio_format_frame_size(&dc.in_audio_format) == 0); if (dc.command == DECODE_COMMAND_STOP || dc.command == DECODE_COMMAND_SEEK || length == 0) return dc.command; /* send stream tags */ if (update_stream_tag(decoder, is)) { enum decoder_command cmd; if (decoder->decoder_tag != NULL) { /* merge with tag from decoder plugin */ struct tag *tag; tag = tag_merge(decoder->stream_tag, decoder->decoder_tag); cmd = do_send_tag(decoder, is, tag); tag_free(tag); } else /* send only the stream tag */ cmd = do_send_tag(decoder, is, decoder->stream_tag); if (cmd != DECODE_COMMAND_NONE) return cmd; } if (!audio_format_equals(&dc.in_audio_format, &dc.out_audio_format)) { data = pcm_convert(&decoder->conv_state, &dc.in_audio_format, data, length, &dc.out_audio_format, &length); /* under certain circumstances, pcm_convert() may return an empty buffer - this condition should be investigated further, but for now, do this check as a workaround: */ if (data == NULL) return DECODE_COMMAND_NONE; } while (length > 0) { struct music_chunk *chunk; char *dest; size_t nbytes; bool full; chunk = decoder_get_chunk(decoder, is); if (chunk == NULL) { assert(dc.command != DECODE_COMMAND_NONE); return dc.command; } dest = music_chunk_write(chunk, &dc.out_audio_format, data_time, bitRate, &nbytes); if (dest == NULL) { /* the chunk is full, flush it */ decoder_flush_chunk(decoder); notify_signal(&pc.notify); continue; } assert(nbytes > 0); if (nbytes > length) nbytes = length; /* copy the buffer */ memcpy(dest, data, nbytes); /* apply replay gain or normalization */ if (replay_gain_info != NULL && replay_gain_mode != REPLAY_GAIN_OFF) replay_gain_apply(replay_gain_info, dest, nbytes, &dc.out_audio_format); else if (normalizationEnabled) normalizeData(dest, nbytes, &dc.out_audio_format); /* expand the music pipe chunk */ full = music_chunk_expand(chunk, &dc.out_audio_format, nbytes); if (full) { /* the chunk is full, flush it */ decoder_flush_chunk(decoder); notify_signal(&pc.notify); } data += nbytes; length -= nbytes; } return DECODE_COMMAND_NONE; }