/* Read database to memory */ int xs_sldb_read(xs_sldb_t *db, const char *dbFilename) { FILE *inFile; char inLine[XS_BUF_SIZE]; size_t lineNum; sldb_node_t *tmnode; assert(db); /* Try to open the file */ if ((inFile = fopen(dbFilename, "r")) == NULL) { xs_error("Could not open SongLengthDB '%s'\n", dbFilename); return -1; } /* Read and parse the data */ lineNum = 0; while (fgets(inLine, XS_BUF_SIZE, inFile) != NULL) { size_t linePos = 0; lineNum++; xs_findnext(inLine, &linePos); /* Check if it is datafield */ if (isxdigit(inLine[linePos])) { /* Check the length of the hash */ int hashLen; for (hashLen = 0; inLine[linePos] && isxdigit(inLine[linePos]); hashLen++, linePos++); if (hashLen != XS_MD5HASH_LENGTH_CH) { xs_error("Invalid MD5-hash in SongLengthDB file '%s' line #%d!\n", dbFilename, (int)lineNum); } else { /* Parse and add node to db */ if ((tmnode = xs_sldb_read_entry(inLine)) != NULL) { xs_sldb_node_insert(db, tmnode); } else { xs_error("Invalid entry in SongLengthDB file '%s' line #%d!\n", dbFilename, (int)lineNum); } } } else if ((inLine[linePos] != ';') && (inLine[linePos] != '[') && (inLine[linePos] != 0)) { xs_error("Invalid line in SongLengthDB file '%s' line #%d\n", dbFilename, (int)lineNum); } } /* Close the file */ fclose(inFile); return 0; }
/* * Initialization functions */ void xs_reinit(void) { XSDEBUG("xs_reinit() thread = %p\n", g_thread_self()); XS_MUTEX_LOCK(xs_status); XS_MUTEX_LOCK(xs_cfg); /* Initialize status and sanitize configuration */ memset(&xs_status, 0, sizeof(xs_status)); if (xs_cfg.audioFrequency < 8000) xs_cfg.audioFrequency = 8000; if (xs_cfg.oversampleFactor < XS_MIN_OVERSAMPLE) xs_cfg.oversampleFactor = XS_MIN_OVERSAMPLE; else if (xs_cfg.oversampleFactor > XS_MAX_OVERSAMPLE) xs_cfg.oversampleFactor = XS_MAX_OVERSAMPLE; if (xs_cfg.audioChannels != XS_CHN_MONO) xs_cfg.oversampleEnable = FALSE; xs_status.audioFrequency = xs_cfg.audioFrequency; xs_status.audioBitsPerSample = xs_cfg.audioBitsPerSample; xs_status.audioChannels = xs_cfg.audioChannels; xs_status.audioFormat = -1; xs_status.oversampleEnable = xs_cfg.oversampleEnable; xs_status.oversampleFactor = xs_cfg.oversampleFactor; /* Try to initialize emulator engine */ xs_init_emu_engine(&xs_cfg.playerEngine, &xs_status); /* Get settings back, in case the chosen emulator backend changed them */ xs_cfg.audioFrequency = xs_status.audioFrequency; xs_cfg.audioBitsPerSample = xs_status.audioBitsPerSample; xs_cfg.audioChannels = xs_status.audioChannels; xs_cfg.oversampleEnable = xs_status.oversampleEnable; XS_MUTEX_UNLOCK(xs_status); XS_MUTEX_UNLOCK(xs_cfg); /* Initialize song-length database */ xs_songlen_close(); if (xs_cfg.songlenDBEnable && (xs_songlen_init() != 0)) { xs_error("Error initializing song-length database!\n"); } /* Initialize STIL database */ xs_stil_close(); if (xs_cfg.stilDBEnable && (xs_stil_init() != 0)) { xs_error("Error initializing STIL database!\n"); } }
/* * Initialization functions */ bool_t xs_init(void) { bool_t success; /* Initialize and get configuration */ xs_init_configuration(); pthread_mutex_lock(&xs_status_mutex); pthread_mutex_lock(&xs_cfg_mutex); /* Initialize status and sanitize configuration */ memset(&xs_status, 0, sizeof(xs_status)); if (xs_cfg.audioFrequency < 8000) xs_cfg.audioFrequency = 8000; xs_status.audioFrequency = xs_cfg.audioFrequency; xs_status.audioChannels = xs_cfg.audioChannels; /* Try to initialize emulator engine */ success = xs_sidplayfp_init(&xs_status); /* Get settings back, in case the chosen emulator backend changed them */ xs_cfg.audioFrequency = xs_status.audioFrequency; xs_cfg.audioChannels = xs_status.audioChannels; pthread_mutex_unlock(&xs_status_mutex); pthread_mutex_unlock(&xs_cfg_mutex); if (! success) return FALSE; /* Initialize song-length database */ xs_songlen_close(); if (xs_cfg.songlenDBEnable && (xs_songlen_init() != 0)) { xs_error("Error initializing song-length database!\n"); } /* Initialize STIL database */ xs_stil_close(); if (xs_cfg.stilDBEnable && (xs_stil_init() != 0)) { xs_error("Error initializing STIL database!\n"); } return TRUE; }
static void XS_STILDB_ERR(gint linenum, gchar *line, const gchar *fmt, ...) { va_list ap; va_start(ap, fmt); xs_verror(fmt, ap); va_end(ap); xs_error("#%d: '%s'\n", linenum, line); }
/* Database handling functions */ static gboolean xs_stildb_node_realloc(stil_node_t *node, gint nsubTunes) { if (node == NULL) return FALSE; /* Re-allocate subTune structure if needed */ if (nsubTunes > node->nsubTunes) { gint clearIndex, clearLength; node->subTunes = (stil_subnode_t **) g_realloc(node->subTunes, (nsubTunes + 1) * sizeof(stil_subnode_t **)); if (!node->subTunes) { xs_error("SubTune pointer structure realloc failed.\n"); return FALSE; } /* Clear the newly allocated memory */ if (node->nsubTunes == 0) { clearIndex = 0; clearLength = nsubTunes + 1; } else { clearIndex = node->nsubTunes + 1; clearLength = (nsubTunes - clearIndex + 1); } memset(&(node->subTunes[clearIndex]), 0, clearLength * sizeof(stil_subnode_t **)); node->nsubTunes = nsubTunes; } /* Allocate memory for subTune */ if (!node->subTunes[nsubTunes]) { node->subTunes[nsubTunes] = g_new0(stil_subnode_t, 1); if (node->subTunes[nsubTunes] == NULL) { xs_error("SubTune structure malloc failed!\n"); return FALSE; } } return TRUE; }
gint xs_stildb_read(xs_stildb_t *db, gchar *filename) { FILE *f; gchar line[XS_BUF_SIZE + 16]; /* Since we add some chars here and there */ stil_node_t *node; gboolean error, multi; gint lineNum, subEntry; gchar *tmpLine = line; assert(db != NULL); /* Try to open the file */ if ((f = fopen(filename, "ra")) == NULL) { xs_error("Could not open STILDB '%s'\n", filename); return -1; } /* Read and parse the data */ lineNum = 0; error = FALSE; multi = FALSE; node = NULL; subEntry = 0; while (!error && fgets(line, XS_BUF_SIZE, f) != NULL) { gsize linePos = 0, eolPos = 0; line[XS_BUF_SIZE] = 0; xs_findeol(line, &eolPos); line[eolPos] = 0; lineNum++; tmpLine = g_convert(line, -1, "UTF-8", XS_STIL_CHARSET, NULL, NULL, NULL); switch (tmpLine[0]) { case '/': /* Check if we are already parsing entry */ multi = FALSE; if (node != NULL) { XS_STILDB_ERR(lineNum, tmpLine, "New entry found before end of current ('%s')!\n", node->filename); xs_stildb_node_free(node); node = NULL; } /* A new node */ subEntry = 0; if ((node = xs_stildb_node_new(tmpLine)) == NULL) { XS_STILDB_ERR(lineNum, tmpLine, "Could not allocate new STILdb-node!\n"); error = TRUE; } break; case '(': /* A new sub-entry */ multi = FALSE; linePos++; if (tmpLine[linePos] == '#') { linePos++; if (isdigit(tmpLine[linePos])) { gsize savePos = linePos; xs_findnum(tmpLine, &linePos); tmpLine[linePos] = 0; subEntry = atol(&tmpLine[savePos]); /* Sanity check */ if (subEntry < 1) { gchar *tmp = node != NULL ? node->filename : NULL; XS_STILDB_ERR(lineNum, tmpLine, "Number of subEntry (%d) for '%s' is invalid\n", subEntry, tmp); subEntry = 0; } } else { XS_STILDB_ERR(lineNum, tmpLine, "Syntax error, expected subEntry number.\n"); subEntry = 0; } } else { XS_STILDB_ERR(lineNum, tmpLine, "Syntax error, expected '#' before subEntry number.\n"); subEntry = 0; } break; case 0: case '#': case '\n': case '\r': /* End of entry/field */ multi = FALSE; if (node != NULL) { xs_stildb_node_insert(db, node); node = NULL; } break; default: /* Check if we are parsing an entry */ xs_findnext(tmpLine, &linePos); if (node == NULL) { XS_STILDB_ERR(lineNum, tmpLine, "Entry data encountered outside of entry or syntax error!\n"); break; } if (!xs_stildb_node_realloc(node, subEntry)) { XS_STILDB_ERR(lineNum, tmpLine, "Could not (re)allocate memory for subEntries!\n"); error = TRUE; break; } /* Some other type */ if (strncmp(tmpLine, " NAME:", 8) == 0) { XS_STILDB_MULTI; g_free(node->subTunes[subEntry]->name); node->subTunes[subEntry]->name = g_strdup(&tmpLine[9]); } else if (strncmp(tmpLine, " TITLE:", 8) == 0) { XS_STILDB_MULTI; multi = TRUE; if (!node->subTunes[subEntry]->title) node->subTunes[subEntry]->title = g_strdup(&tmpLine[9]); xs_pstrcat(&(node->subTunes[subEntry]->info), &tmpLine[2]); } else if (strncmp(tmpLine, " AUTHOR:", 8) == 0) { XS_STILDB_MULTI; g_free(node->subTunes[subEntry]->author); node->subTunes[subEntry]->author = g_strdup(&tmpLine[9]); } else if (strncmp(tmpLine, " ARTIST:", 8) == 0) { XS_STILDB_MULTI; multi = TRUE; xs_pstrcat(&(node->subTunes[subEntry]->info), &tmpLine[1]); } else if (strncmp(tmpLine, "COMMENT:", 8) == 0) { XS_STILDB_MULTI; multi = TRUE; xs_pstrcat(&(node->subTunes[subEntry]->info), tmpLine); } else { if (multi) { xs_pstrcat(&(node->subTunes[subEntry]->info), " "); xs_pstrcat(&(node->subTunes[subEntry]->info), &tmpLine[linePos]); } else { XS_STILDB_ERR(lineNum, tmpLine, "Entry continuation found when multi == FALSE.\n"); } } break; } g_free(tmpLine); } /* while */ /* Check if there is one remaining node */ if (node != NULL) xs_stildb_node_insert(db, node); /* Close the file */ fclose(f); return 0; }
/* * 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; }
static int xs_get_sid_hash(const char *filename, xs_md5hash_t hash) { VFSFile *inFile; xs_md5state_t inState; psidv1_header_t psidH; psidv2_header_t psidH2; uint8_t *songData; uint8_t ib8[2], i8; int index, result; /* Try to open the file */ if ((inFile = vfs_fopen(filename, "rb")) == NULL) return -1; /* Read PSID header in */ if (vfs_fread(psidH.magicID, 1, sizeof psidH.magicID, inFile) < sizeof psidH.magicID) { vfs_fclose(inFile); return -1; } if (strncmp(psidH.magicID, "PSID", 4) && strncmp(psidH.magicID, "RSID", 4)) { vfs_fclose(inFile); xs_error("Not a PSID or RSID file '%s'\n", filename); return -2; } psidH.version = xs_fread_be16(inFile); psidH.dataOffset = xs_fread_be16(inFile); psidH.loadAddress = xs_fread_be16(inFile); psidH.initAddress = xs_fread_be16(inFile); psidH.playAddress = xs_fread_be16(inFile); psidH.nSongs = xs_fread_be16(inFile); psidH.startSong = xs_fread_be16(inFile); psidH.speed = xs_fread_be32(inFile); if (vfs_fread(psidH.sidName, 1, sizeof psidH.sidName, inFile) < sizeof psidH.sidName || vfs_fread(psidH.sidAuthor, 1, sizeof psidH.sidAuthor, inFile) < sizeof psidH.sidAuthor || vfs_fread(psidH.sidCopyright, 1, sizeof psidH.sidCopyright, inFile) < sizeof psidH.sidCopyright) { vfs_fclose(inFile); xs_error("Error reading SID file header from '%s'\n", filename); return -4; } /* Check if we need to load PSIDv2NG header ... */ psidH2.flags = 0; /* Just silence a stupid gcc warning */ if (psidH.version == 2) { /* Yes, we need to */ psidH2.flags = xs_fread_be16(inFile); psidH2.startPage = vfs_getc(inFile); psidH2.pageLength = vfs_getc(inFile); psidH2.reserved = xs_fread_be16(inFile); } /* Allocate buffer */ songData = (uint8_t *) malloc(XS_SIDBUF_SIZE * sizeof(uint8_t)); if (!songData) { vfs_fclose(inFile); xs_error("Error allocating temp data buffer for file '%s'\n", filename); return -3; } /* Read data to buffer */ result = vfs_fread(songData, sizeof(uint8_t), XS_SIDBUF_SIZE, inFile); vfs_fclose(inFile); /* Initialize and start MD5-hash calculation */ xs_md5_init(&inState); if (psidH.loadAddress == 0) { /* Strip load address (2 first bytes) */ xs_md5_append(&inState, &songData[2], result - 2); } else { /* Append "as is" */ xs_md5_append(&inState, songData, result); } /* Free buffer */ free(songData); /* Append header data to hash */ #define XSADDHASH(QDATAB) do { \ ib8[0] = (QDATAB & 0xff); \ ib8[1] = (QDATAB >> 8); \ xs_md5_append(&inState, (uint8_t *) &ib8, sizeof(ib8)); \ } while (0) XSADDHASH(psidH.initAddress); XSADDHASH(psidH.playAddress); XSADDHASH(psidH.nSongs); #undef XSADDHASH /* Append song speed data to hash */ i8 = 0; for (index = 0; (index < psidH.nSongs) && (index < 32); index++) { i8 = (psidH.speed & (1 << index)) ? 60 : 0; xs_md5_append(&inState, &i8, sizeof(i8)); } /* Rest of songs (more than 32) */ for (index = 32; index < psidH.nSongs; index++) { xs_md5_append(&inState, &i8, sizeof(i8)); } /* PSIDv2NG specific */ if (psidH.version == 2) { /* SEE SIDPLAY HEADERS FOR INFO */ i8 = (psidH2.flags >> 2) & 3; if (i8 == 2) xs_md5_append(&inState, &i8, sizeof(i8)); }
/* Parse one SLDB definition line, return SLDB node */ sldb_node_t * xs_sldb_read_entry(char *inLine) { size_t linePos; int i; bool_t isOK; sldb_node_t *tmnode; /* Allocate new node */ tmnode = (sldb_node_t *) malloc(sizeof(sldb_node_t)); if (!tmnode) { xs_error("Error allocating new node. Fatal error.\n"); return NULL; } memset(tmnode, 0, sizeof(sldb_node_t)); /* Get hash value */ linePos = 0; for (i = 0; i < XS_MD5HASH_LENGTH; i++, linePos += 2) { unsigned tmpu; sscanf(&inLine[linePos], "%2x", &tmpu); tmnode->md5Hash[i] = tmpu; } /* Get playtimes */ if (inLine[linePos] != 0) { if (inLine[linePos] != '=') { xs_error("'=' expected on column #%d.\n", (int)linePos); xs_sldb_node_free(tmnode); return NULL; } else { size_t tmpLen, savePos; /* First playtime is after '=' */ savePos = ++linePos; tmpLen = strlen(inLine); /* Get number of sub-tune lengths */ isOK = TRUE; while ((linePos < tmpLen) && isOK) { xs_findnext(inLine, &linePos); if (xs_sldb_gettime(inLine, &linePos) >= 0) tmnode->nlengths++; else isOK = FALSE; } /* Allocate memory for lengths */ if (tmnode->nlengths > 0) { tmnode->lengths = (int *) malloc(tmnode->nlengths * sizeof(int)); if (!tmnode->lengths) { xs_error("Could not allocate memory for node.\n"); xs_sldb_node_free(tmnode); return NULL; } memset(tmnode->lengths, 0, tmnode->nlengths * sizeof(int)); } else { xs_sldb_node_free(tmnode); return NULL; } /* Read lengths in */ i = 0; linePos = savePos; isOK = TRUE; while ((linePos < tmpLen) && (i < tmnode->nlengths) && isOK) { int l; xs_findnext(inLine, &linePos); l = xs_sldb_gettime(inLine, &linePos); if (l >= 0) tmnode->lengths[i] = l; else isOK = FALSE; i++; } if (!isOK) { xs_sldb_node_free(tmnode); return NULL; } else return tmnode; } } xs_sldb_node_free(tmnode); return NULL; }
/* * Start playing the given file */ gboolean xs_play_file(InputPlayback *pb, const gchar *filename, VFSFile *file, gint start_time, gint stop_time, gboolean pause) { xs_tuneinfo_t *tmpTune; gint audioBufSize, bufRemaining, tmpLength, subTune = -1; gchar *audioBuffer = NULL, *oversampleBuffer = NULL; Tuple *tmpTuple; assert(pb); assert(xs_status.sidPlayer != NULL); uri_parse (filename, NULL, NULL, NULL, & subTune); /* Get tune information */ XS_MUTEX_LOCK(xs_status); if (! (xs_status.tuneInfo = xs_status.sidPlayer->plrGetSIDInfo (filename))) { XS_MUTEX_UNLOCK(xs_status); return FALSE; } /* Initialize the tune */ if (! xs_status.sidPlayer->plrLoadSID (& xs_status, filename)) { XS_MUTEX_UNLOCK(xs_status); xs_tuneinfo_free(xs_status.tuneInfo); xs_status.tuneInfo = NULL; return FALSE; } gboolean 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; XSDEBUG("subtune #%i selected (#%d wanted), initializing...\n", xs_status.currSong, subTune); gint channels = (xs_status.audioChannels == XS_CHN_AUTOPAN) ? 2 : xs_status.audioChannels; /* Allocate audio buffer */ audioBufSize = xs_status.audioFrequency * channels * xs_status.audioBitsPerSample / (8 * 4); if (audioBufSize < 512) audioBufSize = 512; audioBuffer = (gchar *) g_malloc(audioBufSize); if (audioBuffer == NULL) { xs_error("Couldn't allocate memory for audio data buffer!\n"); XS_MUTEX_UNLOCK(xs_status); goto xs_err_exit; } if (xs_status.oversampleEnable) { oversampleBuffer = (gchar *) g_malloc(audioBufSize * xs_status.oversampleFactor); if (oversampleBuffer == NULL) { xs_error("Couldn't allocate memory for audio oversampling buffer!\n"); XS_MUTEX_UNLOCK(xs_status); 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_status.sidPlayer->plrInitSong(&xs_status)) { xs_error("Couldn't initialize SID-tune '%s' (sub-tune #%i)!\n", tmpTune->sidFilename, xs_status.currSong); XS_MUTEX_UNLOCK(xs_status); goto xs_err_exit; } /* Open the audio output */ XSDEBUG("open audio output (%d, %d, %d)\n", xs_status.audioFormat, xs_status.audioFrequency, channels); if (!pb->output->open_audio(xs_status.audioFormat, xs_status.audioFrequency, channels)) { xs_error("Couldn't open audio output (fmt=%x, freq=%i, nchan=%i)!\n", xs_status.audioFormat, xs_status.audioFrequency, channels); XS_MUTEX_UNLOCK(xs_status); goto xs_err_exit; } /* Set song information for current subtune */ XSDEBUG("foobar #1\n"); xs_status.sidPlayer->plrUpdateSIDInfo(&xs_status); tmpTuple = tuple_new_from_filename(tmpTune->sidFilename); xs_get_song_tuple_info(tmpTuple, tmpTune, xs_status.currSong); xs_status.stop_flag = FALSE; XS_MUTEX_UNLOCK(xs_status); pb->set_tuple(pb, tmpTuple); pb->set_params (pb, -1, xs_status.audioFrequency, channels); pb->set_pb_ready(pb); XSDEBUG("playing\n"); while (1) { XS_MUTEX_LOCK (xs_status); if (xs_status.stop_flag) { XS_MUTEX_UNLOCK (xs_status); break; } XS_MUTEX_UNLOCK (xs_status); /* Render audio data */ if (xs_status.oversampleEnable) { /* Perform oversampled rendering */ bufRemaining = xs_status.sidPlayer->plrFillBuffer( &xs_status, oversampleBuffer, (audioBufSize * xs_status.oversampleFactor)); bufRemaining /= xs_status.oversampleFactor; /* Execute rate-conversion with filtering */ if (xs_filter_rateconv(audioBuffer, oversampleBuffer, xs_status.audioFormat, xs_status.oversampleFactor, bufRemaining) < 0) { xs_error("Oversampling rate-conversion pass failed.\n"); goto xs_err_exit; } } else { bufRemaining = xs_status.sidPlayer->plrFillBuffer( &xs_status, audioBuffer, audioBufSize); } pb->output->write_audio (audioBuffer, bufRemaining); /* Check if we have played enough */ if (xs_cfg.playMaxTimeEnable) { if (xs_cfg.playMaxTimeUnknown) { if (tmpLength < 0 && pb->output->written_time() >= xs_cfg.playMaxTime * 1000) break; } else { if (pb->output->written_time() >= xs_cfg.playMaxTime * 1000) break; } } if (tmpLength >= 0) { if (pb->output->written_time() >= tmpLength * 1000) break; } } DONE: XSDEBUG("out of playing loop\n"); g_free(audioBuffer); g_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. */ XS_MUTEX_LOCK(xs_status); xs_status.stop_flag = TRUE; /* Free tune information */ xs_status.sidPlayer->plrDeleteSID(&xs_status); xs_tuneinfo_free(xs_status.tuneInfo); xs_status.tuneInfo = NULL; XS_MUTEX_UNLOCK(xs_status); /* Exit the playing thread */ XSDEBUG("exiting thread, bye.\n"); return ! error; xs_err_exit: error = TRUE; goto DONE; }