void error_flac_stream(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { // flac_data * fd = (flac_data *) client_data; UNREFERENCED_PARAMETER(client_data); UNREFERENCED_PARAMETER(decoder); MV_Printf("%s\n", FLAC__StreamDecoderErrorStatusString[status]); // FLAC__stream_decoder_flush(fd->stream); }
void DirectSoundDrv_PCM_Lock(void) { DWORD err; err = WaitForSingleObject(mutex, INFINITE); if (err != WAIT_OBJECT_0) { if (MV_Printf) MV_Printf( "DirectSound lock: wfso %d\n", (int32_t) err); } }
static DWORD WINAPI fillDataThread(LPVOID lpParameter) { DWORD waitret, waitret2; HANDLE handles[] = { handles[0] = notifyPositions[0].hEventNotify, handles[1] = notifyPositions[1].hEventNotify, handles[2] = notifyPositions[2].hEventNotify }; UNREFERENCED_PARAMETER(lpParameter); do { waitret = WaitForMultipleObjects(3, handles, FALSE, INFINITE); switch (waitret) { case WAIT_OBJECT_0: case WAIT_OBJECT_0+1: waitret2 = WaitForSingleObject(mutex, INFINITE); if (waitret2 == WAIT_OBJECT_0) { FillBuffer(WAIT_OBJECT_0 + 1 - waitret); ReleaseMutex(mutex); } else { if (MV_Printf) MV_Printf( "DirectSound fillDataThread: wfso err %d\n", (int32_t) waitret2); } break; case WAIT_OBJECT_0+2: // initprintf( "DirectSound fillDataThread: exiting\n"); ExitThread(0); break; default: if (MV_Printf) MV_Printf( "DirectSound fillDataThread: wfmo err %d\n", (int32_t) waitret); break; } } while (1); return 0; }
static void FillBuffer(int32_t bufnum) { HRESULT err; LPVOID ptr, ptr2; DWORD remaining, remaining2; int32_t retries = 1; //initprintf( "DirectSound FillBuffer: filling %d\n", bufnum); do { err = IDirectSoundBuffer_Lock(lpdsbsec, notifyPositions[bufnum].dwOffset, notifyPositions[1].dwOffset, &ptr, &remaining, &ptr2, &remaining2, 0); if (FAILED(err)) { if (err == DSERR_BUFFERLOST) { err = IDirectSoundBuffer_Restore(lpdsbsec); if (FAILED(err)) { return; } if (retries-- > 0) { continue; } } if (MV_Printf) MV_Printf("DirectSound FillBuffer: err %x\n", (uint32_t) err); return; } break; } while (1); if (ptr) { FillBufferPortion((char *) ptr, remaining); } if (ptr2) { FillBufferPortion((char *) ptr2, remaining2); } IDirectSoundBuffer_Unlock(lpdsbsec, ptr, remaining, ptr2, remaining2); }
static void TeardownDSound(HRESULT err) { if (FAILED(err)) { if (MV_Printf) MV_Printf( "Dying error: %x\n", (uint32_t) err); } if (lpdsnotify) IDirectSoundNotify_Release(lpdsnotify); if (notifyPositions[0].hEventNotify) CloseHandle(notifyPositions[0].hEventNotify); if (notifyPositions[1].hEventNotify) CloseHandle(notifyPositions[1].hEventNotify); if (notifyPositions[2].hEventNotify) CloseHandle(notifyPositions[2].hEventNotify); if (mutex) CloseHandle(mutex); if (lpdsbsec) IDirectSoundBuffer_Release(lpdsbsec); if (lpdsbprimary) IDirectSoundBuffer_Release(lpdsbprimary); if (lpds) IDirectSound_Release(lpds); notifyPositions[0].hEventNotify = notifyPositions[1].hEventNotify = notifyPositions[2].hEventNotify = 0; mutex = NULL; lpdsnotify = NULL; lpdsbsec = NULL; lpdsbprimary = NULL; lpds = NULL; }
static VoiceNode *MV_GetVoice(int32_t handle) { if (handle < MV_MINVOICEHANDLE || handle > MV_MaxVoices) { if (MV_Printf) MV_Printf("MV_GetVoice(): bad handle (%d)!\n", handle); return NULL; } DisableInterrupts(); for (VoiceNode *voice = VoiceList.next; voice != &VoiceList; voice = voice->next) { if (handle == voice->handle) { RestoreInterrupts(); return voice; } } RestoreInterrupts(); MV_SetErrorCode(MV_VoiceNotFound); return NULL; }
int32_t MV_PlayFLAC ( char *ptr, uint32_t ptrlength, int32_t loopstart, int32_t loopend, int32_t pitchoffset, int32_t vol, int32_t left, int32_t right, int32_t priority, uint32_t callbackval ) { VoiceNode *voice; flac_data * fd = 0; FLAC__Metadata_Chain* metadata_chain; UNREFERENCED_PARAMETER(loopend); if ( !MV_Installed ) { MV_SetErrorCode( MV_NotInstalled ); return MV_Error; } fd = (flac_data *) malloc( sizeof(flac_data) ); if (!fd) { MV_SetErrorCode( MV_InvalidFLACFile ); return MV_Error; } memset(fd, 0, sizeof(flac_data)); fd->ptr = ptr; fd->pos = 0; fd->blocksize = 0; fd->length = ptrlength; fd->block = NULL; fd->stream = FLAC__stream_decoder_new(); fd->sample_pos = 0; FLAC__stream_decoder_set_metadata_ignore_all(fd->stream); if (FLAC__stream_decoder_init_stream(fd->stream, read_flac_stream, seek_flac_stream, tell_flac_stream, length_flac_stream, eof_flac_stream, write_flac_stream, /*metadata_flac_stream*/ NULL, error_flac_stream, (void*) fd) != FLAC__STREAM_DECODER_INIT_STATUS_OK) { MV_Printf("MV_PlayFLAC: %s\n", FLAC__stream_decoder_get_resolved_state_string(fd->stream)); MV_SetErrorCode( MV_InvalidFLACFile ); return MV_Error; } // Request a voice from the voice pool voice = MV_AllocVoice( priority ); if ( voice == NULL ) { FLAC__stream_decoder_finish(fd->stream); FLAC__stream_decoder_delete(fd->stream); free(fd); MV_SetErrorCode( MV_NoVoices ); return MV_Error; } fd->owner = voice; voice->wavetype = FLAC; voice->extra = (void*)fd; voice->GetSound = MV_GetNextFLACBlock; voice->NextBlock = fd->block; voice->DemandFeed = NULL; voice->LoopCount = 0; voice->BlockLength = 0; voice->PitchScale = PITCH_GetScale( pitchoffset ); voice->next = NULL; voice->prev = NULL; voice->priority = priority; voice->callbackval = callbackval; voice->Playing = TRUE; voice->Paused = FALSE; voice->LoopStart = 0; voice->LoopEnd = 0; voice->LoopSize = (loopstart >= 0 ? 1 : 0); // parse metadata // loop parsing designed with multiple repetitions in mind // In retrospect, it may be possible to MV_GetVorbisCommentLoops(voice, (vorbis_comment *) tags->data.vorbis_comment) // but libvorbisfile may be confused by the signedness of char* vs FLAC__byte* and this code does not depend on HAVE_VORBIS. metadata_chain = FLAC__metadata_chain_new(); if (metadata_chain != NULL) { if (FLAC__metadata_chain_read_with_callbacks(metadata_chain, fd, flac_callbacks)) { FLAC__Metadata_Iterator* metadata_iterator = FLAC__metadata_iterator_new(); if (metadata_iterator != NULL) { char *vc_loopstart = NULL; char *vc_loopend = NULL; char *vc_looplength = NULL; FLAC__metadata_iterator_init(metadata_iterator, metadata_chain); do { FLAC__StreamMetadata *tags = FLAC__metadata_iterator_get_block(metadata_iterator); if (tags->type == FLAC__METADATA_TYPE_STREAMINFO) { const FLAC__StreamMetadata_StreamInfo *info = &tags->data.stream_info; if (info->channels != 1 && info->channels != 2) { FLAC__metadata_object_delete(tags); FLAC__metadata_iterator_delete(metadata_iterator); // FLAC__metadata_chain_delete(metadata_chain); FLAC__stream_decoder_finish(fd->stream); FLAC__stream_decoder_delete(fd->stream); free(fd); MV_SetErrorCode( MV_InvalidFLACFile ); return MV_Error; } voice->channels = info->channels; voice->bits = info->bits_per_sample; voice->SamplingRate = info->sample_rate; } // load loop tags from metadata if (tags->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { FLAC__uint32 comment; uint8_t loopTagCount; for (comment = 0; comment < tags->data.vorbis_comment.num_comments; ++comment) { const char *entry = (const char *) tags->data.vorbis_comment.comments[comment].entry; if (entry != NULL && entry[0] != '\0') { const char *value = strchr(entry,'='); const size_t field = value-entry; value += 1; for (loopTagCount = 0; loopTagCount < loopStartTagCount && vc_loopstart == NULL; ++loopTagCount) if (strncasecmp(entry, loopStartTags[loopTagCount], field) == 0) vc_loopstart = strdup(value); for (loopTagCount = 0; loopTagCount < loopEndTagCount && vc_loopend == NULL; ++loopTagCount) if (strncasecmp(entry, loopEndTags[loopTagCount], field) == 0) vc_loopend = strdup(value); for (loopTagCount = 0; loopTagCount < loopLengthTagCount && vc_looplength == NULL; ++loopTagCount) if (strncasecmp(entry, loopLengthTags[loopTagCount], field) == 0) vc_looplength = strdup(value); } } } FLAC__metadata_object_delete(tags); // If it wasn't for this I would assign pointers instead of strdup(). } while (FLAC__metadata_iterator_next(metadata_iterator)); if (vc_loopstart != NULL) { { const FLAC__int64 flac_loopstart = atol(vc_loopstart); if (flac_loopstart >= 0) // a loop starting at 0 is valid { voice->LoopStart = (const char *) (intptr_t) flac_loopstart; voice->LoopSize = 1; } } free(vc_loopstart); } if (vc_loopend != NULL) { if (voice->LoopSize > 0) { const FLAC__int64 flac_loopend = atol(vc_loopend); if (flac_loopend > 0) // a loop ending at 0 is invalid voice->LoopEnd = (const char *) (intptr_t) flac_loopend; } free(vc_loopend); } if (vc_looplength != NULL) { if (voice->LoopSize > 0 && voice->LoopEnd == 0) { const FLAC__int64 flac_looplength = atol(vc_looplength); if (flac_looplength > 0) // a loop of length 0 is invalid voice->LoopEnd = (const char *) ((intptr_t) flac_looplength + (intptr_t) voice->LoopStart); } free(vc_looplength); } FLAC__metadata_iterator_delete(metadata_iterator); } else MV_Printf("Error allocating FLAC__Metadata_Iterator!\n"); } else MV_Printf("%s\n", FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(metadata_chain)]); // FLAC__metadata_chain_delete(metadata_chain); // when run with GDB, this throws SIGTRAP about freed heap memory being modified } else MV_Printf("Error allocating FLAC__Metadata_Chain!\n"); // CODEDUP multivoc.c MV_SetVoicePitch voice->RateScale = ( voice->SamplingRate * voice->PitchScale ) / MV_MixRate; voice->FixedPointBufferSize = ( voice->RateScale * MV_MIXBUFFERSIZE ) - voice->RateScale; MV_SetVoiceMixMode( voice ); MV_SetVoiceVolume( voice, vol, left, right ); MV_PlayVoice( voice ); return voice->handle; }
static playbackstatus MV_GetNextFLACBlock ( VoiceNode *voice ) { flac_data * fd = (flac_data *) voice->extra; FLAC__StreamDecoderState decode_state; // FLAC__bool decode_status; voice->Playing = TRUE; if ((FLAC__uint64)(uintptr_t)voice->LoopEnd > 0 && fd->sample_pos >= (FLAC__uint64)(uintptr_t)voice->LoopEnd) if (!FLAC__stream_decoder_seek_absolute(fd->stream, (FLAC__uint64)(uintptr_t)voice->LoopStart)) MV_Printf("MV_GetNextFLACBlock FLAC__stream_decoder_seek_absolute: LOOP_START %ul, LOOP_END %ul\n", (FLAC__uint64)(uintptr_t)voice->LoopStart, (FLAC__uint64)(uintptr_t)voice->LoopEnd); /*decode_status =*/ FLAC__stream_decoder_process_single(fd->stream); decode_state = FLAC__stream_decoder_get_state(fd->stream); /* if (!decode_status) { MV_Printf("MV_GetNextFLACBlock: %s\n", FLAC__StreamDecoderStateString[decode_state]); voice->Playing = FALSE; return NoMoreData; } */ if (decode_state == FLAC__STREAM_DECODER_SEEK_ERROR) { FLAC__stream_decoder_flush(fd->stream); decode_state = FLAC__stream_decoder_get_state(fd->stream); } if (decode_state == FLAC__STREAM_DECODER_END_OF_STREAM) { if (voice->LoopSize > 0) { if (!FLAC__stream_decoder_seek_absolute(fd->stream, (FLAC__uint64)(uintptr_t)voice->LoopStart)) MV_Printf("MV_GetNextFLACBlock FLAC__stream_decoder_seek_absolute: LOOP_START %ul\n", (FLAC__uint64)(uintptr_t)voice->LoopStart); } else { voice->Playing = FALSE; return NoMoreData; } } #if 0 // unnecessary: duplicated in write_flac_stream() voice->channels = FLAC__stream_decoder_get_channels(fd->stream); voice->bits = FLAC__stream_decoder_get_bits_per_sample(fd->stream); voice->SamplingRate = FLAC__stream_decoder_get_sample_rate(fd->stream); // CODEDUP multivoc.c MV_SetVoicePitch voice->RateScale = ( voice->SamplingRate * voice->PitchScale ) / MV_MixRate; voice->FixedPointBufferSize = ( voice->RateScale * MV_MIXBUFFERSIZE ) - voice->RateScale; MV_SetVoiceMixMode( voice ); #endif return KeepPlaying; }
int32_t MV_PlayLoopedVorbis ( char *ptr, uint32_t ptrlength, int32_t loopstart, int32_t loopend, int32_t pitchoffset, int32_t vol, int32_t left, int32_t right, int32_t priority, uint32_t callbackval ) { VoiceNode *voice; int32_t status; vorbis_data * vd = 0; vorbis_info * vi = 0; UNREFERENCED_PARAMETER(loopend); if ( !MV_Installed ) { MV_SetErrorCode( MV_NotInstalled ); return( MV_Error ); } vd = (vorbis_data *) malloc( sizeof(vorbis_data) ); if (!vd) { MV_SetErrorCode( MV_InvalidVorbisFile ); return MV_Error; } memset(vd, 0, sizeof(vorbis_data)); vd->ptr = ptr; vd->pos = 0; vd->length = ptrlength; vd->lastbitstream = -1; status = ov_open_callbacks((void *) vd, &vd->vf, 0, 0, vorbis_callbacks); if (status < 0) { MV_Printf("MV_PlayLoopedVorbis: err %d\n", status); MV_SetErrorCode( MV_InvalidVorbisFile ); return MV_Error; } vi = ov_info(&vd->vf, 0); if (!vi) { ov_clear(&vd->vf); free(vd); MV_SetErrorCode( MV_InvalidVorbisFile ); return MV_Error; } if (vi->channels != 1 && vi->channels != 2) { ov_clear(&vd->vf); free(vd); MV_SetErrorCode( MV_InvalidVorbisFile ); return MV_Error; } // Request a voice from the voice pool voice = MV_AllocVoice( priority ); if ( voice == NULL ) { ov_clear(&vd->vf); free(vd); MV_SetErrorCode( MV_NoVoices ); return( MV_Error ); } voice->wavetype = Vorbis; voice->bits = 16; voice->channels = vi->channels; voice->extra = (void *) vd; voice->GetSound = MV_GetNextVorbisBlock; voice->NextBlock = vd->block; voice->DemandFeed = NULL; voice->LoopCount = 0; voice->BlockLength = 0; voice->PitchScale = PITCH_GetScale( pitchoffset ); voice->length = 0; voice->next = NULL; voice->prev = NULL; voice->priority = priority; voice->callbackval = callbackval; voice->LoopStart = (char *) (loopstart >= 0 ? TRUE : FALSE); voice->LoopEnd = 0; voice->LoopSize = 0; voice->Playing = TRUE; voice->Paused = FALSE; voice->SamplingRate = vi->rate; voice->RateScale = ( voice->SamplingRate * voice->PitchScale ) / MV_MixRate; voice->FixedPointBufferSize = ( voice->RateScale * MixBufferSize ) - voice->RateScale; MV_SetVoiceMixMode( voice ); MV_SetVoiceVolume( voice, vol, left, right ); MV_PlayVoice( voice ); return( voice->handle ); }
static playbackstatus MV_GetNextVorbisBlock ( VoiceNode *voice ) { vorbis_data * vd = (vorbis_data *) voice->extra; int32_t bytes, bytesread; int32_t bitstream, err; voice->Playing = TRUE; bytesread = 0; do { bytes = ov_read(&vd->vf, vd->block + bytesread, BLOCKSIZE - bytesread, 0, 2, 1, &bitstream); //fprintf(stderr, "ov_read = %d\n", bytes); if (bytes > 0) { bytesread += bytes; continue; } else if (bytes == OV_HOLE) continue; else if (bytes == 0) { if (voice->LoopStart) { err = ov_pcm_seek_page(&vd->vf, 0); if (err != 0) { MV_Printf("MV_GetNextVorbisBlock ov_pcm_seek_page_lap: err %d\n", err); } else { continue; } } else { break; } } else if (bytes < 0) { MV_Printf("MV_GetNextVorbisBlock ov_read: err %d\n", bytes); voice->Playing = FALSE; return NoMoreData; } } while (bytesread < BLOCKSIZE); if (bytesread == 0) { voice->Playing = FALSE; return NoMoreData; } if (bitstream != vd->lastbitstream) { vorbis_info * vi = 0; vi = ov_info(&vd->vf, -1); if (!vi || (vi->channels != 1 && vi->channels != 2)) { voice->Playing = FALSE; return NoMoreData; } voice->channels = vi->channels; voice->SamplingRate = vi->rate; voice->RateScale = ( voice->SamplingRate * voice->PitchScale ) / MV_MixRate; voice->FixedPointBufferSize = ( voice->RateScale * MixBufferSize ) - voice->RateScale; MV_SetVoiceMixMode( voice ); vd->lastbitstream = bitstream; } bytesread /= 2 * voice->channels; voice->position = 0; voice->sound = vd->block; voice->BlockLength = 0; voice->length = bytesread << 16; return( KeepPlaying ); }
/*--------------------------------------------------------------------- JBF: no synchronisation happens inside MV_ServiceVoc nor the supporting functions it calls. This would cause a deadlock between the mixer thread in the driver vs the nested locking in the user-space functions of MultiVoc. The call to MV_ServiceVoc is synchronised in the driver. ---------------------------------------------------------------------*/ static void MV_ServiceVoc(void) { // Toggle which buffer we'll mix next if (++MV_MixPage >= MV_NumberOfBuffers) MV_MixPage -= MV_NumberOfBuffers; if (MV_ReverbLevel == 0) { // Initialize buffer //Commented out so that the buffer is always cleared. //This is so the guys at Echo Speech can mix into the //buffer even when no sounds are playing. if (!MV_BufferEmpty[MV_MixPage]) { Bmemset(MV_MixBuffer[MV_MixPage], 0, MV_BufferSize); MV_BufferEmpty[ MV_MixPage ] = TRUE; } } else { char const *const end = MV_MixBuffer[0] + MV_BufferLength; char *dest = MV_MixBuffer[MV_MixPage]; char const *source = MV_MixBuffer[MV_MixPage] - MV_ReverbDelay; if (source < MV_MixBuffer[ 0 ]) source += MV_BufferLength; int32_t length = MV_BufferSize; while (length > 0) { int const count = (source + length > end) ? (end - source) : length; MV_16BitReverb(source, dest, MV_ReverbTable, count / 2); // if we go through the loop again, it means that we've wrapped around the buffer source = MV_MixBuffer[ 0 ]; dest += count; length -= count; } } // Play any waiting voices //DisableInterrupts(); VoiceNode *voice; if (!VoiceList.next || (voice = VoiceList.next) == &VoiceList) return; int iter = 0; VoiceNode *next; do { next = voice->next; if (++iter > MV_MaxVoices && MV_Printf) MV_Printf("more iterations than voices! iter: %d\n",iter); if (voice->Paused) continue; MV_BufferEmpty[ MV_MixPage ] = FALSE; MV_Mix(voice, MV_MixPage); // Is this voice done? if (!voice->Playing) { //JBF: prevent a deadlock caused by MV_StopVoice grabbing the mutex again //MV_StopVoice( voice ); LL_Remove(voice, next, prev); LL_Add((VoiceNode*) &VoicePool, voice, next, prev); switch (voice->wavetype) { #ifdef HAVE_VORBIS case FMT_VORBIS: MV_ReleaseVorbisVoice(voice); break; #endif #ifdef HAVE_FLAC case FMT_FLAC: MV_ReleaseFLACVoice(voice); break; #endif case FMT_XA: MV_ReleaseXAVoice(voice); break; default: break; } voice->handle = 0; if (MV_CallBackFunc) MV_CallBackFunc(voice->callbackval); } } while ((voice = next) != &VoiceList); //RestoreInterrupts(); }