/* ==================== ModPlug_FetchSound ==================== */ static const snd_buffer_t* ModPlug_FetchSound (void *sfxfetcher, void **chfetcherpointer, unsigned int *start, unsigned int nbsampleframes) { modplug_stream_perchannel_t* per_ch = (modplug_stream_perchannel_t *)*chfetcherpointer; modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfxfetcher; snd_buffer_t* sb; int newlength, done, ret; unsigned int real_start; unsigned int factor; // If there's no fetcher structure attached to the channel yet if (per_ch == NULL) { size_t buff_len, memsize; snd_format_t sb_format; sb_format.speed = snd_renderbuffer->format.speed; sb_format.width = per_sfx->format.width; sb_format.channels = per_sfx->format.channels; buff_len = STREAM_BUFFER_SIZE(&sb_format); memsize = sizeof (*per_ch) - sizeof (per_ch->sb.samples) + buff_len; per_ch = (modplug_stream_perchannel_t *)Mem_Alloc (snd_mempool, memsize); // Open it with the modplugFile API per_ch->mf = qModPlug_Load(per_sfx->file, per_sfx->filesize); if (!per_ch->mf) { Con_Printf("error while reading ModPlug stream \"%s\"\n", per_sfx->name); Mem_Free (per_ch); return NULL; } #ifndef SND_MODPLUG_STATIC if(qModPlug_SetMasterVolume) #endif qModPlug_SetMasterVolume(per_ch->mf, 512); // max volume, DP scales down! per_ch->bs = 0; per_ch->sb_offset = 0; per_ch->sb.format = sb_format; per_ch->sb.nbframes = 0; per_ch->sb.maxframes = buff_len / (per_ch->sb.format.channels * per_ch->sb.format.width); *chfetcherpointer = per_ch; } real_start = *start; sb = &per_ch->sb; factor = per_sfx->format.width * per_sfx->format.channels; // If the stream buffer can't contain that much samples anyway if (nbsampleframes > sb->maxframes) { Con_Printf ("ModPlug_FetchSound: stream buffer too small (%u sample frames required)\n", nbsampleframes); return NULL; } // If the data we need has already been decompressed in the sfxbuffer, just return it if (per_ch->sb_offset <= real_start && per_ch->sb_offset + sb->nbframes >= real_start + nbsampleframes) { *start = per_ch->sb_offset; return sb; } newlength = (int)(per_ch->sb_offset + sb->nbframes) - real_start; // If we need to skip some data before decompressing the rest, or if the stream has looped if (newlength < 0 || per_ch->sb_offset > real_start) { unsigned int time_start; unsigned int modplug_start; /* MODs loop on their own, so any position is valid! if (real_start > (unsigned int)per_sfx->total_length) { Con_Printf ("ModPlug_FetchSound: asked for a start position after the end of the sfx! (%u > %u)\n", real_start, per_sfx->total_length); return NULL; } */ // We work with 200ms (1/5 sec) steps to avoid rounding errors time_start = real_start * 5 / snd_renderbuffer->format.speed; modplug_start = time_start * (1000 / 5); Con_DPrintf("warning: mod file needed to seek (to %d)\n", modplug_start); qModPlug_Seek(per_ch->mf, modplug_start); sb->nbframes = 0; real_start = (unsigned int) ((float)modplug_start / 1000 * snd_renderbuffer->format.speed); if (*start - real_start + nbsampleframes > sb->maxframes) { Con_Printf ("ModPlug_FetchSound: stream buffer too small after seek (%u sample frames required)\n", *start - real_start + nbsampleframes); per_ch->sb_offset = real_start; return NULL; } } // Else, move forward the samples we need to keep in the sound buffer else { memmove (sb->samples, sb->samples + (real_start - per_ch->sb_offset) * factor, newlength * factor); sb->nbframes = newlength; } per_ch->sb_offset = real_start; // We add more than one frame of sound to the buffer: // 1- to ensure we won't lose many samples during the resampling process // 2- to reduce calls to ModPlug_FetchSound to regulate workload newlength = (int)(per_sfx->format.speed*STREAM_BUFFER_FILL); if ((size_t) ((double) newlength * (double)sb->format.speed / (double)per_sfx->format.speed) + sb->nbframes > sb->maxframes) { Con_Printf ("ModPlug_FetchSound: stream buffer overflow (%u + %u = %u sample frames / %u)\n", (unsigned int) ((double) newlength * (double)sb->format.speed / (double)per_sfx->format.speed), sb->nbframes, (unsigned int) ((double) newlength * (double)sb->format.speed / (double)per_sfx->format.speed) + sb->nbframes, sb->maxframes); return NULL; } newlength *= factor; // convert from sample frames to bytes if(newlength > (int)sizeof(resampling_buffer)) newlength = sizeof(resampling_buffer); // Decompress in the resampling_buffer done = 0; while ((ret = qModPlug_Read (per_ch->mf, (char *)&resampling_buffer[done], (int)(newlength - done))) > 0) done += ret; if(done < newlength) { // Argh. We didn't get as many samples as we wanted. Probably // libmodplug forgot what mLoopCount==-1 means... basically, this means // we can't loop like this. Try to let DP fix it later... per_sfx->sfx->total_length = (real_start + ((size_t)done / (size_t)factor)); per_sfx->sfx->loopstart = 0; if(newlength != done) Con_DPrintf("ModPlug_Fetch: wanted: %d, got: %d\n", newlength, done); } Snd_AppendToSndBuffer (sb, resampling_buffer, (size_t)done / (size_t)factor, &per_sfx->format); *start = per_ch->sb_offset; return sb; }
/* ==================== ModPlug_GetSamplesFloat ==================== */ static void ModPlug_GetSamplesFloat(channel_t *ch, sfx_t *sfx, int firstsampleframe, int numsampleframes, float *outsamplesfloat) { modplug_stream_perchannel_t* per_ch = (modplug_stream_perchannel_t *)ch->fetcher_data; modplug_stream_persfx_t* per_sfx = (modplug_stream_persfx_t *)sfx->fetcher_data; int newlength, done, ret; int f = sfx->format.width * sfx->format.channels; // bytes per frame short *buf; int i, len; // If there's no fetcher structure attached to the channel yet if (per_ch == NULL) { per_ch = (modplug_stream_perchannel_t *)Mem_Alloc(snd_mempool, sizeof(*per_ch)); // Open it with the modplugFile API per_ch->mf = qModPlug_Load(per_sfx->file, per_sfx->filesize); if (!per_ch->mf) { // we can't call Con_Printf here, not thread safe // Con_Printf("error while reading ModPlug stream \"%s\"\n", per_sfx->name); Mem_Free(per_ch); return; } #ifndef LINK_TO_LIBMODPLUG if(qModPlug_SetMasterVolume) #endif qModPlug_SetMasterVolume(per_ch->mf, 512); // max volume, DP scales down! per_ch->bs = 0; per_ch->buffer_firstframe = 0; per_ch->buffer_numframes = 0; ch->fetcher_data = per_ch; } // if the request is too large for our buffer, loop... while (numsampleframes * f > (int)sizeof(per_ch->buffer)) { done = sizeof(per_ch->buffer) / f; ModPlug_GetSamplesFloat(ch, sfx, firstsampleframe, done, outsamplesfloat); firstsampleframe += done; numsampleframes -= done; outsamplesfloat += done * sfx->format.channels; } // seek if the request is before the current buffer (loop back) // seek if the request starts beyond the current buffer by at least one frame (channel was zero volume for a while) // do not seek if the request overlaps the buffer end at all (expected behavior) if (per_ch->buffer_firstframe > firstsampleframe || per_ch->buffer_firstframe + per_ch->buffer_numframes < firstsampleframe) { // we expect to decode forward from here so this will be our new buffer start per_ch->buffer_firstframe = firstsampleframe; per_ch->buffer_numframes = 0; // we don't actually seek - we don't care much about timing on silent mod music streams and looping never happens //qModPlug_Seek(per_ch->mf, firstsampleframe * 1000.0 / sfx->format.speed); } // decompress the file as needed if (firstsampleframe + numsampleframes > per_ch->buffer_firstframe + per_ch->buffer_numframes) { // first slide the buffer back, discarding any data preceding the range we care about int offset = firstsampleframe - per_ch->buffer_firstframe; int keeplength = per_ch->buffer_numframes - offset; if (keeplength > 0) memmove(per_ch->buffer, per_ch->buffer + offset * sfx->format.width * sfx->format.channels, keeplength * sfx->format.width * sfx->format.channels); per_ch->buffer_firstframe = firstsampleframe; per_ch->buffer_numframes -= offset; // decompress as much as we can fit in the buffer newlength = sizeof(per_ch->buffer) - per_ch->buffer_numframes * f; done = 0; while (newlength > done && (ret = qModPlug_Read(per_ch->mf, (void *)((unsigned char *)per_ch->buffer + done), (int)(newlength - done))) > 0) done += ret; // clear the missing space if any if (done < newlength) { memset(per_ch->buffer + done, 0, newlength - done); // Argh. We didn't get as many samples as we wanted. Probably // libmodplug forgot what mLoopCount==-1 means... basically, this means // we can't loop like this. Try to let DP fix it later... sfx->total_length = firstsampleframe + done / f; sfx->loopstart = 0; // can't Con_Printf from this thread //if (newlength != done) // Con_DPrintf("ModPlug_Fetch: wanted: %d, got: %d\n", newlength, done); } // we now have more data in the buffer per_ch->buffer_numframes += done / f; } // convert the sample format for the caller buf = (short *)(per_ch->buffer + (firstsampleframe - per_ch->buffer_firstframe) * f); len = numsampleframes * sfx->format.channels; for (i = 0;i < len;i++) outsamplesfloat[i] = buf[i] * (1.0f / 32768.0f); }