Cog Audio: Now preserves already resampled output when switching output formats
This commit is contained in:
parent
ee509b6e13
commit
92d29e7acf
6 changed files with 129 additions and 12 deletions
|
@ -15,6 +15,7 @@
|
||||||
#import <audio/audio_resampler.h>
|
#import <audio/audio_resampler.h>
|
||||||
|
|
||||||
#import "Node.h"
|
#import "Node.h"
|
||||||
|
#import "RefillNode.h"
|
||||||
|
|
||||||
@interface ConverterNode : Node {
|
@interface ConverterNode : Node {
|
||||||
NSDictionary * rgInfo;
|
NSDictionary * rgInfo;
|
||||||
|
@ -49,6 +50,11 @@
|
||||||
AudioStreamBasicDescription floatFormat;
|
AudioStreamBasicDescription floatFormat;
|
||||||
AudioStreamBasicDescription dmFloatFormat; // downmixed/upmixed float format
|
AudioStreamBasicDescription dmFloatFormat; // downmixed/upmixed float format
|
||||||
AudioStreamBasicDescription outputFormat;
|
AudioStreamBasicDescription outputFormat;
|
||||||
|
|
||||||
|
AudioStreamBasicDescription previousOutputFormat;
|
||||||
|
AudioStreamBasicDescription rememberedInputFormat;
|
||||||
|
RefillNode *refillNode;
|
||||||
|
id __weak originalPreviousNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithController:(id)c previous:(id)p;
|
- (id)initWithController:(id)c previous:(id)p;
|
||||||
|
|
|
@ -59,6 +59,9 @@ void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
|
||||||
latencyStarted = -1;
|
latencyStarted = -1;
|
||||||
latencyEaten = 0;
|
latencyEaten = 0;
|
||||||
latencyPostfill = NO;
|
latencyPostfill = NO;
|
||||||
|
|
||||||
|
refillNode = nil;
|
||||||
|
originalPreviousNode = nil;
|
||||||
|
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
|
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputResampling" options:0 context:nil];
|
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputResampling" options:0 context:nil];
|
||||||
|
@ -415,6 +418,17 @@ static void extrapolate(float *buffer, size_t channels, size_t frameSize, size_t
|
||||||
usleep(500);
|
usleep(500);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
else if (refillNode)
|
||||||
|
{
|
||||||
|
// refill node just ended, file resumes
|
||||||
|
[self setPreviousNode:originalPreviousNode];
|
||||||
|
[self setEndOfStream:NO];
|
||||||
|
[self setShouldContinue:YES];
|
||||||
|
refillNode = nil;
|
||||||
|
[self cleanUp];
|
||||||
|
[self setupWithInputFormat:rememberedInputFormat outputFormat:outputFormat];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
[self writeData:writeBuf amount:amountConverted];
|
[self writeData:writeBuf amount:amountConverted];
|
||||||
|
@ -429,7 +443,6 @@ static void extrapolate(float *buffer, size_t channels, size_t frameSize, size_t
|
||||||
int extrapolateStart = 0;
|
int extrapolateStart = 0;
|
||||||
int extrapolateEnd = 0;
|
int extrapolateEnd = 0;
|
||||||
size_t amountToSkip = 0;
|
size_t amountToSkip = 0;
|
||||||
BOOL endOfInputStream = NO;
|
|
||||||
|
|
||||||
if (stopping)
|
if (stopping)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -530,7 +543,12 @@ tryagain:
|
||||||
size_t bytesRead = [self readData:inputBuffer + amountToSkip + bytesReadFromInput amount:(int)(amountToWrite - bytesReadFromInput)];
|
size_t bytesRead = [self readData:inputBuffer + amountToSkip + bytesReadFromInput amount:(int)(amountToWrite - bytesReadFromInput)];
|
||||||
bytesReadFromInput += bytesRead;
|
bytesReadFromInput += bytesRead;
|
||||||
if (!bytesRead)
|
if (!bytesRead)
|
||||||
usleep(500);
|
{
|
||||||
|
if (refillNode)
|
||||||
|
[self setEndOfStream:YES];
|
||||||
|
else
|
||||||
|
usleep(500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesReadFromInput += amountToSkip;
|
bytesReadFromInput += amountToSkip;
|
||||||
|
@ -612,7 +630,7 @@ tryagain:
|
||||||
inpOffset = 0;
|
inpOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (floatOffset == floatSize)
|
if (inpOffset != inpSize && floatOffset == floatSize)
|
||||||
{
|
{
|
||||||
struct resampler_data src_data;
|
struct resampler_data src_data;
|
||||||
|
|
||||||
|
@ -875,6 +893,7 @@ static float db_to_scale(float db)
|
||||||
- (void)setOutputFormat:(AudioStreamBasicDescription)format
|
- (void)setOutputFormat:(AudioStreamBasicDescription)format
|
||||||
{
|
{
|
||||||
DLog(@"SETTING OUTPUT FORMAT!");
|
DLog(@"SETTING OUTPUT FORMAT!");
|
||||||
|
previousOutputFormat = outputFormat;
|
||||||
outputFormat = format;
|
outputFormat = format;
|
||||||
outputFormatChanged = YES;
|
outputFormatChanged = YES;
|
||||||
}
|
}
|
||||||
|
@ -884,13 +903,34 @@ static float db_to_scale(float db)
|
||||||
DLog(@"FORMAT CHANGED");
|
DLog(@"FORMAT CHANGED");
|
||||||
paused = YES;
|
paused = YES;
|
||||||
[self cleanUp];
|
[self cleanUp];
|
||||||
if (outputFormatChanged)
|
if (outputFormatChanged && ![buffer isEmpty])
|
||||||
{
|
{
|
||||||
[writeLock lock];
|
// Transfer previously buffered data, remember input format
|
||||||
[buffer empty];
|
rememberedInputFormat = format;
|
||||||
[writeLock unlock];
|
originalPreviousNode = previousNode;
|
||||||
|
refillNode = [[RefillNode alloc] initWithController:controller previous:nil];
|
||||||
|
[self setPreviousNode:refillNode];
|
||||||
|
|
||||||
|
int dataRead = 0;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
void * ptr;
|
||||||
|
dataRead = [buffer lengthAvailableToReadReturningPointer:&ptr];
|
||||||
|
if (dataRead) {
|
||||||
|
[refillNode writeData:(float*)ptr floatCount:dataRead / sizeof(float)];
|
||||||
|
[buffer didReadLength:dataRead];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self setupWithInputFormat:previousOutputFormat outputFormat:outputFormat];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[self setupWithInputFormat:format outputFormat:outputFormat];
|
||||||
}
|
}
|
||||||
[self setupWithInputFormat:format outputFormat:outputFormat];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setRGInfo:(NSDictionary *)rgi
|
- (void)setRGInfo:(NSDictionary *)rgi
|
||||||
|
|
|
@ -91,7 +91,7 @@
|
||||||
{
|
{
|
||||||
format = *f;
|
format = *f;
|
||||||
// Calculate a ratio and add to double(seconds) instead, as format may change
|
// Calculate a ratio and add to double(seconds) instead, as format may change
|
||||||
double oldSampleRatio = sampleRatio;
|
// double oldSampleRatio = sampleRatio;
|
||||||
sampleRatio = 1.0 / (format.mSampleRate * format.mBytesPerPacket);
|
sampleRatio = 1.0 / (format.mSampleRate * format.mBytesPerPacket);
|
||||||
BufferChain *bufferChain = [controller bufferChain];
|
BufferChain *bufferChain = [controller bufferChain];
|
||||||
if (bufferChain)
|
if (bufferChain)
|
||||||
|
@ -102,8 +102,13 @@
|
||||||
// This clears the resampler buffer, but not the input buffer
|
// This clears the resampler buffer, but not the input buffer
|
||||||
// We also have to jump the play position ahead accounting for
|
// We also have to jump the play position ahead accounting for
|
||||||
// the data we are flushing
|
// the data we are flushing
|
||||||
|
#if 0
|
||||||
|
// We no longer need to do this, because outputchanged converter
|
||||||
|
// now uses the RefillNode to slap the previous samples into
|
||||||
|
// itself
|
||||||
if (oldSampleRatio)
|
if (oldSampleRatio)
|
||||||
amountPlayed += oldSampleRatio * [[converter buffer] bufferedLength];
|
amountPlayed += oldSampleRatio * [[converter buffer] bufferedLength];
|
||||||
|
#endif
|
||||||
[converter setOutputFormat:format];
|
[converter setOutputFormat:format];
|
||||||
[converter inputFormatDidChange:[bufferChain inputFormat]];
|
[converter inputFormatDidChange:[bufferChain inputFormat]];
|
||||||
}
|
}
|
||||||
|
|
26
Audio/Chain/RefillNode.h
Normal file
26
Audio/Chain/RefillNode.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// RefillNode.h
|
||||||
|
// Cog
|
||||||
|
//
|
||||||
|
// Created by Christopher SNowhill on 1/13/22.
|
||||||
|
// Copyright 2022 __LoSnoCo__. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
#import <CoreAudio/AudioHardware.h>
|
||||||
|
#import <AudioToolbox/AudioToolbox.h>
|
||||||
|
#import <AudioUnit/AudioUnit.h>
|
||||||
|
|
||||||
|
#import "Node.h"
|
||||||
|
#import "Plugin.h"
|
||||||
|
|
||||||
|
#define INPUT_NODE_SEEK
|
||||||
|
|
||||||
|
@interface RefillNode : Node {
|
||||||
|
// This node just slaps pre-converted data into its buffer for re-buffering
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)writeData:(float *)data floatCount:(size_t)count;
|
||||||
|
|
||||||
|
@end
|
34
Audio/Chain/RefillNode.m
Normal file
34
Audio/Chain/RefillNode.m
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
//
|
||||||
|
// RefillNode.m
|
||||||
|
// Cog
|
||||||
|
//
|
||||||
|
// Created by Christopher Snowhill on 1/13/22.
|
||||||
|
// Copyright 2022 __LoSnoCo__. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "Plugin.h"
|
||||||
|
#import "RefillNode.h"
|
||||||
|
|
||||||
|
#import "Logging.h"
|
||||||
|
|
||||||
|
@implementation RefillNode
|
||||||
|
|
||||||
|
- (id)initWithController:(id)c previous:(id)p {
|
||||||
|
self = [super initWithController:c previous:p];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (void)writeData:(float *)data floatCount:(size_t)count
|
||||||
|
{
|
||||||
|
[self writeData:data amount:(int)(count * sizeof(float))];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
DLog(@"Refill Node dealloc");
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -87,6 +87,8 @@
|
||||||
8389F2A4278E646E0074164C /* lpc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8389F2A2278E646E0074164C /* lpc.c */; };
|
8389F2A4278E646E0074164C /* lpc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8389F2A2278E646E0074164C /* lpc.c */; };
|
||||||
839366671815923C006DD712 /* CogPluginMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = 839366651815923C006DD712 /* CogPluginMulti.h */; };
|
839366671815923C006DD712 /* CogPluginMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = 839366651815923C006DD712 /* CogPluginMulti.h */; };
|
||||||
839366681815923C006DD712 /* CogPluginMulti.m in Sources */ = {isa = PBXBuildFile; fileRef = 839366661815923C006DD712 /* CogPluginMulti.m */; };
|
839366681815923C006DD712 /* CogPluginMulti.m in Sources */ = {isa = PBXBuildFile; fileRef = 839366661815923C006DD712 /* CogPluginMulti.m */; };
|
||||||
|
83A44A01279119B50049B6E2 /* RefillNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A449FF279119B50049B6E2 /* RefillNode.m */; };
|
||||||
|
83A44A02279119B50049B6E2 /* RefillNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A44A00279119B50049B6E2 /* RefillNode.h */; };
|
||||||
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
|
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
|
||||||
8E8D3D2F0CBAEE6E00135C1B /* AudioContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
8E8D3D2F0CBAEE6E00135C1B /* AudioContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E8D3D2E0CBAEE6E00135C1B /* AudioContainer.m */; };
|
8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E8D3D2E0CBAEE6E00135C1B /* AudioContainer.m */; };
|
||||||
|
@ -203,6 +205,8 @@
|
||||||
8389F2A2278E646E0074164C /* lpc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpc.c; sourceTree = "<group>"; };
|
8389F2A2278E646E0074164C /* lpc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpc.c; sourceTree = "<group>"; };
|
||||||
839366651815923C006DD712 /* CogPluginMulti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CogPluginMulti.h; sourceTree = "<group>"; };
|
839366651815923C006DD712 /* CogPluginMulti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CogPluginMulti.h; sourceTree = "<group>"; };
|
||||||
839366661815923C006DD712 /* CogPluginMulti.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CogPluginMulti.m; sourceTree = "<group>"; };
|
839366661815923C006DD712 /* CogPluginMulti.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CogPluginMulti.m; sourceTree = "<group>"; };
|
||||||
|
83A449FF279119B50049B6E2 /* RefillNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RefillNode.m; sourceTree = "<group>"; };
|
||||||
|
83A44A00279119B50049B6E2 /* RefillNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RefillNode.h; sourceTree = "<group>"; };
|
||||||
8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
8DC2EF5B0486A6940098B216 /* CogAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CogAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
8DC2EF5B0486A6940098B216 /* CogAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CogAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContainer.h; sourceTree = "<group>"; };
|
8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContainer.h; sourceTree = "<group>"; };
|
||||||
|
@ -328,6 +332,8 @@
|
||||||
17D21C750B8BE4BA00D1EBDE /* Chain */ = {
|
17D21C750B8BE4BA00D1EBDE /* Chain */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
83A44A00279119B50049B6E2 /* RefillNode.h */,
|
||||||
|
83A449FF279119B50049B6E2 /* RefillNode.m */,
|
||||||
17D21C760B8BE4BA00D1EBDE /* BufferChain.h */,
|
17D21C760B8BE4BA00D1EBDE /* BufferChain.h */,
|
||||||
17D21C770B8BE4BA00D1EBDE /* BufferChain.m */,
|
17D21C770B8BE4BA00D1EBDE /* BufferChain.m */,
|
||||||
8EC1225D0B993BD500C5B3AD /* ConverterNode.h */,
|
8EC1225D0B993BD500C5B3AD /* ConverterNode.h */,
|
||||||
|
@ -748,6 +754,7 @@
|
||||||
8389F292278E64590074164C /* s16_to_float.h in Headers */,
|
8389F292278E64590074164C /* s16_to_float.h in Headers */,
|
||||||
8389F279278E64590074164C /* utf.h in Headers */,
|
8389F279278E64590074164C /* utf.h in Headers */,
|
||||||
8389F288278E64590074164C /* retro_environment.h in Headers */,
|
8389F288278E64590074164C /* retro_environment.h in Headers */,
|
||||||
|
83A44A02279119B50049B6E2 /* RefillNode.h in Headers */,
|
||||||
8EC1225F0B993BD500C5B3AD /* ConverterNode.h in Headers */,
|
8EC1225F0B993BD500C5B3AD /* ConverterNode.h in Headers */,
|
||||||
8389F28C278E64590074164C /* file_stream.h in Headers */,
|
8389F28C278E64590074164C /* file_stream.h in Headers */,
|
||||||
8384912718080FF100E7332D /* Logging.h in Headers */,
|
8384912718080FF100E7332D /* Logging.h in Headers */,
|
||||||
|
@ -850,6 +857,7 @@
|
||||||
17C940240B900909008627D6 /* AudioMetadataReader.m in Sources */,
|
17C940240B900909008627D6 /* AudioMetadataReader.m in Sources */,
|
||||||
17B619310B909BC300BC003F /* AudioPropertiesReader.m in Sources */,
|
17B619310B909BC300BC003F /* AudioPropertiesReader.m in Sources */,
|
||||||
17ADB13D0B97926D00257CA2 /* AudioSource.m in Sources */,
|
17ADB13D0B97926D00257CA2 /* AudioSource.m in Sources */,
|
||||||
|
83A44A01279119B50049B6E2 /* RefillNode.m in Sources */,
|
||||||
8EC122600B993BD500C5B3AD /* ConverterNode.m in Sources */,
|
8EC122600B993BD500C5B3AD /* ConverterNode.m in Sources */,
|
||||||
8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */,
|
8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */,
|
||||||
B0575F300D687A4000411D77 /* Helper.m in Sources */,
|
B0575F300D687A4000411D77 /* Helper.m in Sources */,
|
||||||
|
@ -998,9 +1006,7 @@
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = "HAVE_CONFIG_H=1";
|
||||||
"HAVE_CONFIG_H=1",
|
|
||||||
);
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
|
Loading…
Reference in a new issue