mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-22 12:33:29 +00:00
Here, at last, is _some_ audio output, at least.
This commit is contained in:
parent
afde8dac49
commit
38ffcaa262
@ -493,7 +493,7 @@ void Machine::set_key_state(Key key, bool isPressed)
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::Speaker::get_sample_range(uint64_t start_time, int number_of_samples, uint16_t *target)
|
||||
void Machine::Speaker::get_sample_range(uint64_t start_time, int number_of_samples, int16_t *target)
|
||||
{
|
||||
if(!_is_enabled)
|
||||
{
|
||||
@ -501,7 +501,7 @@ void Machine::Speaker::get_sample_range(uint64_t start_time, int number_of_sampl
|
||||
}
|
||||
else
|
||||
{
|
||||
*target = ((start_time / _divider)&1) ? 255 : 0;
|
||||
*target = ((start_time / (_divider+1))&1) ? 255 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -513,5 +513,5 @@ void Machine::Speaker::set_divider(uint8_t divider)
|
||||
|
||||
void Machine::Speaker::set_is_enabled(bool is_enabled)
|
||||
{
|
||||
_is_enabled = false;
|
||||
_is_enabled = is_enabled;
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ class Machine: public CPU6502::Processor<Machine> {
|
||||
void set_is_enabled(bool is_enabled);
|
||||
inline bool get_is_enabled() { return _is_enabled; }
|
||||
|
||||
void get_sample_range(uint64_t start_time, int number_of_samples, uint16_t *target);
|
||||
void get_sample_range(uint64_t start_time, int number_of_samples, int16_t *target);
|
||||
|
||||
private:
|
||||
uint8_t _divider;
|
||||
|
@ -7,6 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
4B0EBFB81C487F2F00A11F35 /* AudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B0EBFB71C487F2F00A11F35 /* AudioQueue.m */; };
|
||||
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; };
|
||||
4B14145D1B5887A600E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; };
|
||||
4B14145E1B5887AA00E04248 /* CPU6502AllRAM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414591B58879D00E04248 /* CPU6502AllRAM.cpp */; };
|
||||
@ -321,6 +322,8 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
4B0EBFB61C487F2F00A11F35 /* AudioQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioQueue.h; sourceTree = "<group>"; };
|
||||
4B0EBFB71C487F2F00A11F35 /* AudioQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioQueue.m; sourceTree = "<group>"; };
|
||||
4B1414501B58848C00E04248 /* ClockSignal-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ClockSignal-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
4B1414571B58879D00E04248 /* CPU6502.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPU6502.cpp; sourceTree = "<group>"; };
|
||||
4B1414581B58879D00E04248 /* CPU6502.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CPU6502.hpp; sourceTree = "<group>"; };
|
||||
@ -738,6 +741,8 @@
|
||||
4B55CE521C3B7ABF0093A61B /* CSElectron.h */,
|
||||
4B55CE531C3B7ABF0093A61B /* CSElectron.mm */,
|
||||
4BAE587D1C447B7A005B9AF0 /* KeyCodes.h */,
|
||||
4B0EBFB61C487F2F00A11F35 /* AudioQueue.h */,
|
||||
4B0EBFB71C487F2F00A11F35 /* AudioQueue.m */,
|
||||
);
|
||||
path = Wrappers;
|
||||
sourceTree = "<group>";
|
||||
@ -1547,6 +1552,7 @@
|
||||
4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */,
|
||||
4B55CE4B1C3B3B0C0093A61B /* CSAtari2600.mm in Sources */,
|
||||
4B55CE581C3B7D360093A61B /* Atari2600Document.swift in Sources */,
|
||||
4B0EBFB81C487F2F00A11F35 /* AudioQueue.m in Sources */,
|
||||
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,
|
||||
4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */,
|
||||
4B55CE4E1C3B3BDA0093A61B /* CSMachine.mm in Sources */,
|
||||
|
@ -6,3 +6,4 @@
|
||||
#import "CSAtari2600.h"
|
||||
#import "CSElectron.h"
|
||||
#import "CSCathodeRayView.h"
|
||||
#import "AudioQueue.h"
|
||||
|
@ -27,6 +27,7 @@ class ElectronDocument: MachineDocument {
|
||||
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
||||
super.windowControllerDidLoadNib(aController)
|
||||
electron.view = openGLView
|
||||
electron.audioQueue = self.audioQueue
|
||||
openGLView.frameBounds = CGRectMake(0.0225, 0.0625, 0.75, 0.75)
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import AudioToolbox
|
||||
|
||||
class MachineDocument: NSDocument, CSCathodeRayViewDelegate, CSCathodeRayViewResponderDelegate {
|
||||
|
||||
@ -17,6 +18,8 @@ class MachineDocument: NSDocument, CSCathodeRayViewDelegate, CSCathodeRayViewRes
|
||||
}
|
||||
}
|
||||
|
||||
lazy var audioQueue = AudioQueue()
|
||||
|
||||
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
||||
super.windowControllerDidLoadNib(aController)
|
||||
|
||||
|
15
OSBindings/Mac/Clock Signal/Wrappers/AudioQueue.h
Normal file
15
OSBindings/Mac/Clock Signal/Wrappers/AudioQueue.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// AudioQueue.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/01/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface AudioQueue : NSObject
|
||||
|
||||
- (void)enqueueAudioBuffer:(const int16_t *)buffer numberOfSamples:(unsigned int)lengthInSamples;
|
||||
|
||||
@end
|
131
OSBindings/Mac/Clock Signal/Wrappers/AudioQueue.m
Normal file
131
OSBindings/Mac/Clock Signal/Wrappers/AudioQueue.m
Normal file
@ -0,0 +1,131 @@
|
||||
//
|
||||
// AudioQueue.m
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/01/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AudioQueue.h"
|
||||
@import AudioToolbox;
|
||||
|
||||
#define AudioQueueNumAudioBuffers 3
|
||||
#define AudioQueueStreamLength 2048
|
||||
#define AudioQueueBufferLength 512
|
||||
|
||||
@implementation AudioQueue
|
||||
{
|
||||
AudioQueueRef _audioQueue;
|
||||
AudioQueueBufferRef _audioBuffers[AudioQueueNumAudioBuffers];
|
||||
unsigned int _audioStreamReadPosition, _audioStreamWritePosition, _queuedAudioStreamSegments;
|
||||
int16_t _audioStream[AudioQueueStreamLength];
|
||||
BOOL _isOutputtingAudio;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark AudioQueue callbacks and setup; for pushing audio out
|
||||
|
||||
- (void)audioQueue:(AudioQueueRef)theAudioQueue didCallbackWithBuffer:(AudioQueueBufferRef)buffer
|
||||
{
|
||||
@synchronized(self)
|
||||
{
|
||||
if(_queuedAudioStreamSegments > AudioQueueNumAudioBuffers-1) _isOutputtingAudio = YES;
|
||||
|
||||
if(_isOutputtingAudio && _queuedAudioStreamSegments)
|
||||
{
|
||||
_queuedAudioStreamSegments--;
|
||||
memcpy(buffer->mAudioData, &_audioStream[_audioStreamReadPosition], buffer->mAudioDataByteSize);
|
||||
_audioStreamReadPosition = (_audioStreamReadPosition + AudioQueueBufferLength)%AudioQueueStreamLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(buffer->mAudioData, 0, buffer->mAudioDataByteSize);
|
||||
_isOutputtingAudio = NO;
|
||||
}
|
||||
AudioQueueEnqueueBuffer(theAudioQueue, buffer, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void audioOutputCallback(
|
||||
void *inUserData,
|
||||
AudioQueueRef inAQ,
|
||||
AudioQueueBufferRef inBuffer)
|
||||
{
|
||||
[(__bridge AudioQueue *)inUserData audioQueue:inAQ didCallbackWithBuffer:inBuffer];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if(self)
|
||||
{
|
||||
/*
|
||||
|
||||
Describe a mono, 16bit, 44.1Khz audio format
|
||||
|
||||
*/
|
||||
AudioStreamBasicDescription outputDescription;
|
||||
|
||||
outputDescription.mSampleRate = 44100;
|
||||
|
||||
outputDescription.mFormatID = kAudioFormatLinearPCM;
|
||||
outputDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
|
||||
outputDescription.mBytesPerPacket = 2;
|
||||
outputDescription.mFramesPerPacket = 1;
|
||||
outputDescription.mBytesPerFrame = 2;
|
||||
outputDescription.mChannelsPerFrame = 1;
|
||||
outputDescription.mBitsPerChannel = 16;
|
||||
|
||||
outputDescription.mReserved = 0;
|
||||
|
||||
// create an audio output queue along those lines
|
||||
if(!AudioQueueNewOutput(
|
||||
&outputDescription,
|
||||
audioOutputCallback,
|
||||
(__bridge void *)(self),
|
||||
NULL,
|
||||
kCFRunLoopCommonModes,
|
||||
0,
|
||||
&_audioQueue))
|
||||
{
|
||||
_audioStreamWritePosition = AudioQueueBufferLength;
|
||||
UInt32 bufferBytes = AudioQueueBufferLength * sizeof(int16_t);
|
||||
|
||||
int c = AudioQueueNumAudioBuffers;
|
||||
while(c--)
|
||||
{
|
||||
AudioQueueAllocateBuffer(_audioQueue, bufferBytes, &_audioBuffers[c]);
|
||||
memset(_audioBuffers[c]->mAudioData, 0, bufferBytes);
|
||||
_audioBuffers[c]->mAudioDataByteSize = bufferBytes;
|
||||
AudioQueueEnqueueBuffer(_audioQueue, _audioBuffers[c], 0, NULL);
|
||||
}
|
||||
|
||||
AudioQueueStart(_audioQueue, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)enqueueAudioBuffer:(const int16_t *)buffer numberOfSamples:(unsigned int)lengthInSamples
|
||||
{
|
||||
@synchronized(self)
|
||||
{
|
||||
memcpy(&_audioStream[_audioStreamWritePosition], buffer, lengthInSamples * sizeof(int16_t));
|
||||
_audioStreamWritePosition = (_audioStreamWritePosition + lengthInSamples)%AudioQueueStreamLength;
|
||||
|
||||
if(_queuedAudioStreamSegments == (AudioQueueStreamLength/AudioQueueBufferLength))
|
||||
{
|
||||
_audioStreamReadPosition = (_audioStreamReadPosition + lengthInSamples)%AudioQueueStreamLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
_queuedAudioStreamSegments++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -19,6 +19,6 @@
|
||||
- (void)perform:(dispatch_block_t)action;
|
||||
|
||||
- (void)crt:(Outputs::CRT *)crt didEndFrame:(CRTFrame *)frame didDetectVSync:(BOOL)didDetectVSync;
|
||||
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const uint16_t *)samples length:(int)length;
|
||||
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length;
|
||||
|
||||
@end
|
||||
|
@ -8,11 +8,13 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "CSCathodeRayView.h"
|
||||
#import "AudioQueue.h"
|
||||
|
||||
@interface CSMachine : NSObject
|
||||
|
||||
- (void)runForNumberOfCycles:(int)numberOfCycles;
|
||||
|
||||
@property (nonatomic, weak) CSCathodeRayView *view;
|
||||
@property (nonatomic, weak) AudioQueue *audioQueue;
|
||||
|
||||
@end
|
||||
|
@ -18,7 +18,7 @@ struct CRTDelegate: public Outputs::CRT::Delegate {
|
||||
|
||||
struct SpeakerDelegate: public Outputs::Speaker::Delegate {
|
||||
__weak CSMachine *machine;
|
||||
void speaker_did_complete_samples(Outputs::Speaker *speaker, const uint16_t *buffer, int buffer_size) {
|
||||
void speaker_did_complete_samples(Outputs::Speaker *speaker, const int16_t *buffer, int buffer_size) {
|
||||
[machine speaker:speaker didCompleteSamples:buffer length:buffer_size];
|
||||
}
|
||||
};
|
||||
@ -44,7 +44,8 @@ typedef NS_ENUM(NSInteger, CSAtari2600RunningState) {
|
||||
if([self.view pushFrame:frame]) crt->return_frame();
|
||||
}
|
||||
|
||||
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const uint16_t *)samples length:(int)length {
|
||||
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length {
|
||||
[self.audioQueue enqueueAudioBuffer:samples numberOfSamples:(unsigned int)length];
|
||||
}
|
||||
|
||||
- (void)runForNumberOfCycles:(int)cycles {
|
||||
|
@ -19,7 +19,7 @@ class Speaker {
|
||||
public:
|
||||
class Delegate {
|
||||
public:
|
||||
virtual void speaker_did_complete_samples(Speaker *speaker, const uint16_t *buffer, int buffer_size) = 0;
|
||||
virtual void speaker_did_complete_samples(Speaker *speaker, const int16_t *buffer, int buffer_size) = 0;
|
||||
};
|
||||
|
||||
void set_output_rate(int cycles_per_second, int buffer_size)
|
||||
@ -28,7 +28,7 @@ class Speaker {
|
||||
if(_buffer_size != buffer_size)
|
||||
{
|
||||
delete[] _buffer_in_progress;
|
||||
_buffer_in_progress = new uint16_t[buffer_size];
|
||||
_buffer_in_progress = new int16_t[buffer_size];
|
||||
_buffer_size = buffer_size;
|
||||
}
|
||||
set_needs_updated_filter_coefficients();
|
||||
@ -52,7 +52,7 @@ class Speaker {
|
||||
}
|
||||
|
||||
protected:
|
||||
uint16_t *_buffer_in_progress;
|
||||
int16_t *_buffer_in_progress;
|
||||
int _buffer_size;
|
||||
int _buffer_in_progress_pointer;
|
||||
int _number_of_taps;
|
||||
|
Loading…
Reference in New Issue
Block a user