static gboolean vorbis_play (const gchar * filename, VFSFile * file) { if (file == NULL) return FALSE; vorbis_info *vi; OggVorbis_File vf; gint last_section = -1; ReplayGainInfo rg_info; gfloat pcmout[PCM_BUFSIZE*sizeof(float)], **pcm; gint bytes, channels, samplerate, br; gchar * title = NULL; memset(&vf, 0, sizeof(vf)); gboolean error = FALSE; if (ov_open_callbacks (file, & vf, NULL, 0, vfs_is_streaming (file) ? vorbis_callbacks_stream : vorbis_callbacks) < 0) { error = TRUE; goto play_cleanup; } vi = ov_info(&vf, -1); if (vi->channels > 2) goto play_cleanup; br = vi->bitrate_nominal; channels = vi->channels; samplerate = vi->rate; aud_input_set_bitrate (br); if (!aud_input_open_audio(FMT_FLOAT, samplerate, channels)) { error = TRUE; goto play_cleanup; } vorbis_update_replaygain(&vf, &rg_info); aud_input_set_gain (& rg_info); /* * Note that chaining changes things here; A vorbis file may * be a mix of different channels, bitrates and sample rates. * You can fetch the information for any section of the file * using the ov_ interface. */ while (! aud_input_check_stop ()) { int seek_value = aud_input_check_seek(); if (seek_value >= 0 && ov_time_seek (& vf, (double) seek_value / 1000) < 0) { fprintf (stderr, "vorbis: seek failed\n"); error = TRUE; break; } gint current_section = last_section; bytes = ov_read_float(&vf, &pcm, PCM_FRAMES, ¤t_section); if (bytes == OV_HOLE) continue; if (bytes <= 0) break; bytes = vorbis_interleave_buffer (pcm, bytes, channels, pcmout); { /* try to detect when metadata has changed */ vorbis_comment * comment = ov_comment (& vf, -1); const gchar * new_title = (comment == NULL) ? NULL : vorbis_comment_query (comment, "title", 0); if (new_title != NULL && (title == NULL || strcmp (title, new_title))) { g_free (title); title = g_strdup (new_title); aud_input_set_tuple (get_tuple_for_vorbisfile (& vf, filename)); } } if (current_section != last_section) { /* * The info struct is different in each section. vf * holds them all for the given bitstream. This * requests the current one */ vi = ov_info(&vf, -1); if (vi->channels > 2) goto stop_processing; if (vi->rate != samplerate || vi->channels != channels) { samplerate = vi->rate; channels = vi->channels; if (!aud_input_open_audio(FMT_FLOAT, vi->rate, vi->channels)) { error = TRUE; goto stop_processing; } vorbis_update_replaygain(&vf, &rg_info); aud_input_set_gain (& rg_info); /* audio reopened */ } } aud_input_write_audio (pcmout, bytes); stop_processing: if (current_section != last_section) { aud_input_set_bitrate (br); last_section = current_section; } } /* main loop */ play_cleanup: ov_clear(&vf); g_free (title); return ! error; }
/* * Start playing the given file */ bool_t xs_play_file(const char *filename, VFSFile *file) { xs_tuneinfo_t *tmpTune; int audioBufSize, bufRemaining, tmpLength, subTune = -1; char *audioBuffer = NULL, *oversampleBuffer = NULL; Tuple *tmpTuple; uri_parse (filename, NULL, NULL, NULL, & subTune); /* Get tune information */ pthread_mutex_lock(&xs_status_mutex); if (! (xs_status.tuneInfo = xs_sidplayfp_getinfo (filename))) { pthread_mutex_unlock(&xs_status_mutex); return FALSE; } /* Initialize the tune */ if (! xs_sidplayfp_load (& xs_status, filename)) { pthread_mutex_unlock(&xs_status_mutex); xs_tuneinfo_free(xs_status.tuneInfo); xs_status.tuneInfo = NULL; return FALSE; } bool_t error = FALSE; /* Set general status information */ tmpTune = xs_status.tuneInfo; if (subTune < 1 || subTune > xs_status.tuneInfo->nsubTunes) xs_status.currSong = xs_status.tuneInfo->startTune; else xs_status.currSong = subTune; int channels = xs_status.audioChannels; /* Allocate audio buffer */ audioBufSize = xs_status.audioFrequency * channels * FMT_SIZEOF (FMT_S16_NE); if (audioBufSize < 512) audioBufSize = 512; audioBuffer = (char *) malloc(audioBufSize); if (audioBuffer == NULL) { xs_error("Couldn't allocate memory for audio data buffer!\n"); pthread_mutex_unlock(&xs_status_mutex); goto xs_err_exit; } /* Check minimum playtime */ tmpLength = tmpTune->subTunes[xs_status.currSong - 1].tuneLength; if (xs_cfg.playMinTimeEnable && (tmpLength >= 0)) { if (tmpLength < xs_cfg.playMinTime) tmpLength = xs_cfg.playMinTime; } /* Initialize song */ if (!xs_sidplayfp_initsong(&xs_status)) { xs_error("Couldn't initialize SID-tune '%s' (sub-tune #%i)!\n", tmpTune->sidFilename, xs_status.currSong); pthread_mutex_unlock(&xs_status_mutex); goto xs_err_exit; } /* Open the audio output */ if (!aud_input_open_audio(FMT_S16_NE, xs_status.audioFrequency, channels)) { xs_error("Couldn't open audio output (fmt=%x, freq=%i, nchan=%i)!\n", FMT_S16_NE, xs_status.audioFrequency, channels); pthread_mutex_unlock(&xs_status_mutex); goto xs_err_exit; } /* Set song information for current subtune */ xs_sidplayfp_updateinfo(&xs_status); tmpTuple = tuple_new_from_filename(tmpTune->sidFilename); xs_get_song_tuple_info(tmpTuple, tmpTune, xs_status.currSong); pthread_mutex_unlock(&xs_status_mutex); aud_input_set_tuple(tmpTuple); while (! aud_input_check_stop ()) { bufRemaining = xs_sidplayfp_fillbuffer(&xs_status, audioBuffer, audioBufSize); aud_input_write_audio (audioBuffer, bufRemaining); /* Check if we have played enough */ if (xs_cfg.playMaxTimeEnable) { if (xs_cfg.playMaxTimeUnknown) { if (tmpLength < 0 && aud_input_written_time() >= xs_cfg.playMaxTime * 1000) break; } else { if (aud_input_written_time() >= xs_cfg.playMaxTime * 1000) break; } } if (tmpLength >= 0) { if (aud_input_written_time() >= tmpLength * 1000) break; } } DONE: free(audioBuffer); free(oversampleBuffer); /* Set playing status to false (stopped), thus when * XMMS next calls xs_get_time(), it can return appropriate * value "not playing" status and XMMS knows to move to * next entry in the playlist .. or whatever it wishes. */ pthread_mutex_lock(&xs_status_mutex); /* Free tune information */ xs_sidplayfp_delete(&xs_status); xs_tuneinfo_free(xs_status.tuneInfo); xs_status.tuneInfo = NULL; pthread_mutex_unlock(&xs_status_mutex); /* Exit the playing thread */ return ! error; xs_err_exit: error = TRUE; goto DONE; }