diff --git a/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj b/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj index 5e7551fc1..fe3e98184 100644 --- a/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj +++ b/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj @@ -148,6 +148,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 8324C584181513A10046F78F /* circular_buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = circular_buffer.h; sourceTree = ""; }; 8343780B17F932B600584396 /* HCDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HCDecoder.h; sourceTree = ""; }; 8343780E17F932C900584396 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../../Audio/Plugin.h; sourceTree = ""; }; 8343784A17F93CB500584396 /* psflib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = psflib.xcodeproj; path = ../../Frameworks/psflib/psflib.xcodeproj; sourceTree = ""; }; @@ -269,6 +270,7 @@ 8360EEED17F92AC8005208A4 /* HighlyComplete */ = { isa = PBXGroup; children = ( + 8324C584181513A10046F78F /* circular_buffer.h */, 834379A717F9818400584396 /* HCDecoder.mm */, 8343786117F944BD00584396 /* hebios.h */, 8343780E17F932C900584396 /* Plugin.h */, diff --git a/Plugins/HighlyComplete/HighlyComplete/HCDecoder.h b/Plugins/HighlyComplete/HighlyComplete/HCDecoder.h index b6ec1baa6..a9a7c18e7 100644 --- a/Plugins/HighlyComplete/HighlyComplete/HCDecoder.h +++ b/Plugins/HighlyComplete/HighlyComplete/HCDecoder.h @@ -8,13 +8,16 @@ #import #import "Plugin.h" +#include "circular_buffer.h" @interface HCDecoder : NSObject { id currentSource; NSString *currentUrl; uint8_t *emulatorCore; void *emulatorExtra; - + + circular_buffer silence_test_buffer; + NSDictionary *metadataList; int tagLengthMs; diff --git a/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm b/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm index 8f741c779..15d103fcb 100644 --- a/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm +++ b/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm @@ -1091,7 +1091,14 @@ static int twosf_info(void * context, const char * name, const char * value) else return NO; framesRead = 0; + + silence_test_buffer.resize( sampleRate * 30 * 2 ); + if (![self fillBuffer]) + return NO; + + silence_test_buffer.remove_leading_silence(1); + return YES; } @@ -1158,15 +1165,23 @@ static int twosf_info(void * context, const char * name, const char * value) return YES; } - -- (int)readAudio:(void *)buf frames:(UInt32)frames +- (BOOL)fillBuffer { - if ( !emulatorCore ) + unsigned long free_space = silence_test_buffer.free_space() / 2; + while ( free_space ) { - if (![self initializeDecoder]) - return 0; + unsigned long samples_to_write = 0; + int16_t * buf = silence_test_buffer.get_write_ptr( samples_to_write ); + int samples_read = [self readAudioInternal:buf frames:(UInt32)samples_to_write / 2] * 2; + if ( !samples_read ) break; + silence_test_buffer.samples_written( samples_read ); + free_space -= samples_read; } - + return !silence_test_buffer.test_silence(); +} + +- (int)readAudioInternal:(void *)buf frames:(UInt32)frames +{ if ( type == 1 || type == 2 ) { uint32_t howmany = frames; @@ -1224,28 +1239,48 @@ static int twosf_info(void * context, const char * name, const char * value) frames = howmany; } - if ( framesRead + frames > framesLength ) { + return frames; +} + +- (int)readAudio:(void *)buf frames:(UInt32)frames +{ + if ( !emulatorCore ) + { + if (![self initializeDecoder]) + return 0; + } + else if ( ![self fillBuffer] ) + return 0; + + unsigned long written = silence_test_buffer.data_available() / 2; + if ( written > frames ) + written = frames; + if ( written > totalFrames - framesRead ) + written = totalFrames - framesRead; + if ( written == 0 ) + return 0; + + silence_test_buffer.read( (int16_t *) buf, written * 2 ); + + if ( framesRead + written > framesLength ) { long fadeStart = (framesLength > framesRead) ? framesLength : framesRead; - long fadeEnd = framesRead + frames; + long fadeEnd = framesRead + written; long fadeTotal = totalFrames - framesLength; long fadePos; int16_t * buf16 = ( int16_t * ) buf; - for (fadePos = fadeStart; fadePos < fadeEnd && fadePos < totalFrames; ++fadePos) { + for (fadePos = fadeStart; fadePos < fadeEnd; ++fadePos) { long scale = totalFrames - fadePos; buf16[ 0 ] = buf16[ 0 ] * scale / fadeTotal; buf16[ 1 ] = buf16[ 1 ] * scale / fadeTotal; buf16 += 2; } - - if (fadePos < fadeEnd) - frames = (int)(fadePos - fadeStart); } - - framesRead += frames; - - return frames; + + framesRead += written; + + return (int) written; } - (void)closeDecoder @@ -1308,6 +1343,20 @@ static int twosf_info(void * context, const char * name, const char * value) return -1; } + unsigned long buffered_samples = silence_test_buffer.data_available() / 2; + if ( buffered_samples >= ( frame - framesRead ) ) + { + frame -= framesRead; + silence_test_buffer.read( NULL, frame * 2 ); + framesRead += frame * 2; + return framesRead; + } + else if ( buffered_samples ) + { + silence_test_buffer.read( NULL, buffered_samples * 2 ); + framesRead += buffered_samples * 2; + } + if ( type == 1 || type == 2 ) { do diff --git a/Plugins/HighlyComplete/HighlyComplete/circular_buffer.h b/Plugins/HighlyComplete/HighlyComplete/circular_buffer.h new file mode 100644 index 000000000..266b6184a --- /dev/null +++ b/Plugins/HighlyComplete/HighlyComplete/circular_buffer.h @@ -0,0 +1,106 @@ +#ifndef _CIRCULAR_BUFFER_H_ +#define _CIRCULAR_BUFFER_H_ + +#include +#include + +long const silence_threshold = 8; + +template +class circular_buffer +{ + std::vector buffer; + unsigned long readptr, writeptr, used, size; + unsigned long silence_count; +public: + circular_buffer() : readptr( 0 ), writeptr( 0 ), size( 0 ), used( 0 ), silence_count( 0 ) { } + unsigned long data_available() { return used; } + unsigned long free_space() { return size - used; } + T* get_write_ptr( unsigned long & count_out ) + { + count_out = size - writeptr; + if ( count_out > size - used ) count_out = size - used; + return &buffer[writeptr]; + } + bool samples_written( unsigned long count ) + { + unsigned long max_count = size - writeptr; + if ( max_count > size - used ) max_count = size - used; + if ( count > max_count ) return false; + silence_count += count_silent( &buffer[ 0 ] + writeptr, &buffer[ 0 ] + writeptr + count ); + used += count; + writeptr = ( writeptr + count ) % size; + return true; + } + unsigned long read( T * dst, unsigned long count ) + { + unsigned long done = 0; + for(;;) + { + unsigned long delta = size - readptr; + if ( delta > used ) delta = used; + if ( delta > count ) delta = count; + if ( !delta ) break; + + if ( dst ) std::copy( buffer.begin() + readptr, buffer.begin() + readptr + delta, dst ); + silence_count -= count_silent( &buffer[ 0 ] + readptr, &buffer[ 0 ] + readptr + delta ); + if ( dst ) dst += delta; + done += delta; + readptr = ( readptr + delta ) % size; + count -= delta; + used -= delta; + } + return done; + } + void reset() + { + readptr = writeptr = used = 0; + } + void resize(unsigned long p_size) + { + size = p_size; + buffer.resize( p_size ); + reset(); + } + bool test_silence() const + { + return silence_count == used; + } + void remove_leading_silence( unsigned long mask ) + { + T const* p; + T const* begin; + T const* end; + mask = ~mask; + if ( used ) + { + p = begin = &buffer[ 0 ] + readptr - 1; + end = &buffer[ 0 ] + ( writeptr > readptr ? writeptr : size ); + while ( ++p < end && ( ( unsigned long ) ( *p + silence_threshold ) <= ( unsigned long ) silence_threshold * 2 ) ); + unsigned long skipped = ( p - begin ) & mask; + silence_count -= skipped; + used -= skipped; + readptr = ( readptr + skipped ) % size; + if ( readptr == 0 && readptr != writeptr ) + { + p = begin = &buffer[ 0 ]; + end = &buffer[ 0 ] + writeptr; + while ( ++p < end && ( ( unsigned long ) ( *p + silence_threshold ) <= ( unsigned long ) silence_threshold * 2 ) ); + skipped = ( p - begin ) & mask; + silence_count -= skipped; + used -= skipped; + readptr += skipped; + } + } + } +private: + static unsigned long count_silent(T const* begin, T const* end) + { + unsigned long count = 0; + T const* p = begin - 1; + while ( ++p < end ) count += ( ( unsigned long ) ( *p + silence_threshold ) <= ( unsigned long ) silence_threshold * 2 ); + return count; + } +}; + +#endif