int32_t MV_Shutdown(void) { if (!MV_Installed) return MV_Ok; MV_KillAllVoices(); MV_Installed = FALSE; // Stop the sound playback engine MV_StopPlayback(); // Shutdown the sound card SoundDriver_Shutdown(); // Free any voices we allocated #ifdef _3DS linearFree(MV_Voices); #else ALIGNED_FREE_AND_NULL(MV_Voices); #endif LL_Reset((VoiceNode*) &VoiceList, next, prev); LL_Reset((VoiceNode*) &VoicePool, next, prev); MV_MaxVoices = 1; // Release the descriptor from our mix buffer for (int buffer = 0; buffer < MV_NUMBEROFBUFFERS; buffer++) MV_MixBuffer[ buffer ] = NULL; MV_SetErrorCode(MV_NotInstalled); return MV_Ok; }
static int32_t MV_StartPlayback(void) { // Initialize the buffers Bmemset(MV_MixBuffer[0], 0, MV_TOTALBUFFERSIZE); for (int buffer = 0; buffer < MV_NumberOfBuffers; buffer++) MV_BufferEmpty[buffer] = TRUE; MV_MixPage = 1; if (SoundDriver_BeginPlayback(MV_MixBuffer[0], MV_BufferSize, MV_NumberOfBuffers, MV_ServiceVoc) != MV_Ok) { MV_SetErrorCode(MV_DriverError); return MV_Error; } return MV_Ok; }
VoiceNode *MV_BeginService(int32_t handle) { if (!MV_Installed) return NULL; DisableInterrupts(); VoiceNode *voice; if ((voice = MV_GetVoice(handle)) == NULL) { RestoreInterrupts(); MV_SetErrorCode(MV_VoiceNotFound); return NULL; } return voice; }
int MV_PlayVorbis3D ( char *ptr, unsigned int ptrlength, int pitchoffset, int angle, int distance, int priority, unsigned int callbackval ) { int left; int right; int mid; int volume; int status; if ( !MV_Installed ) { MV_SetErrorCode( MV_NotInstalled ); return( MV_Error ); } if ( distance < 0 ) { distance = -distance; angle += MV_NumPanPositions / 2; } volume = MIX_VOLUME( distance ); // Ensure angle is within 0 - 31 angle &= MV_MaxPanPosition; left = MV_PanTable[ angle ][ volume ].left; right = MV_PanTable[ angle ][ volume ].right; mid = max( 0, 255 - distance ); status = MV_PlayVorbis( ptr, ptrlength, pitchoffset, mid, left, right, priority, callbackval ); return( status ); }
int32_t MV_PlayFLAC3D ( char *ptr, uint32_t ptrlength, int32_t loophow, int32_t pitchoffset, int32_t angle, int32_t distance, int32_t priority, uint32_t callbackval ) { int32_t left; int32_t right; int32_t mid; int32_t volume; int32_t status; if ( !MV_Installed ) { MV_SetErrorCode( MV_NotInstalled ); return MV_Error; } if ( distance < 0 ) { distance = -distance; angle += MV_NUMPANPOSITIONS / 2; } volume = MIX_VOLUME( distance ); // Ensure angle is within 0 - 127 angle &= MV_MAXPANPOSITION; left = MV_PanTable[ angle ][ volume ].left; right = MV_PanTable[ angle ][ volume ].right; mid = max( 0, 255 - distance ); status = MV_PlayFLAC(ptr, ptrlength, pitchoffset, loophow, -1, mid, left, right, priority, callbackval); return status; }
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; }
int MV_PlayLoopedVorbis ( char *ptr, unsigned int ptrlength, int loopstart, int loopend, int pitchoffset, int vol, int left, int right, int priority, unsigned int callbackval ) { VoiceNode *voice; int status; vorbis_data * vd = 0; vorbis_info * vi = 0; 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) { fprintf(stderr, "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 ); }
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; }
int32_t MV_Init(int32_t soundcard, int32_t MixRate, int32_t Voices, int32_t numchannels, void *initdata) { if (MV_Installed) MV_Shutdown(); MV_SetErrorCode(MV_Ok); // MV_TotalMemory + 2: FIXME, see valgrind_errors.log int const totalmem = Voices * sizeof(VoiceNode) + MV_TOTALBUFFERSIZE + 2; #ifdef _3DS char *ptr = (char *) linearAlloc( totalmem); #else char *ptr = (char *) Xaligned_alloc(16, totalmem); #endif if (!ptr) { MV_SetErrorCode(MV_NoMem); return MV_Error; } Bmemset(ptr, 0, totalmem); MV_Voices = (VoiceNode *)ptr; ptr += Voices * sizeof(VoiceNode); // Set number of voices before calculating volume table MV_MaxVoices = Voices; LL_Reset((VoiceNode*) &VoiceList, next, prev); LL_Reset((VoiceNode*) &VoicePool, next, prev); for (int index = 0; index < Voices; index++) { LL_Add((VoiceNode*) &VoicePool, &MV_Voices[ index ], next, prev); } MV_SetReverseStereo(FALSE); ASS_SoundDriver = soundcard; // Initialize the sound card if (SoundDriver_Init(&MixRate, &numchannels, initdata) != MV_Ok) MV_SetErrorCode(MV_DriverError); if (MV_ErrorCode != MV_Ok) { ALIGNED_FREE_AND_NULL(MV_Voices); return MV_Error; } MV_Installed = TRUE; MV_CallBackFunc = NULL; MV_ReverbLevel = 0; MV_ReverbTable = NULL; // Set the sampling rate MV_MixRate = MixRate; // Set Mixer to play stereo digitized sound MV_SetMixMode(numchannels); MV_ReverbDelay = MV_BufferSize * 3; // Make sure we don't cross a physical page MV_MixBuffer[ MV_NumberOfBuffers ] = ptr; for (int buffer = 0; buffer < MV_NumberOfBuffers; buffer++) { MV_MixBuffer[ buffer ] = ptr; ptr += MV_BufferSize; } // Calculate pan table MV_CalcPanTable(); MV_SetVolume(MV_MAXTOTALVOLUME); // Start the playback engine if (MV_StartPlayback() != MV_Ok) { // Preserve error code while we shutdown. int status = MV_ErrorCode; MV_Shutdown(); MV_SetErrorCode(status); return MV_Error; } return MV_Ok; }