added the start of the audio layers; added schematic

This commit is contained in:
Jorj Bauer 2017-06-28 06:26:41 -04:00
parent 154c1d28b5
commit 2d49ae0efa
20 changed files with 42656 additions and 1 deletions

4
.gitignore vendored
View File

@ -5,4 +5,6 @@ parallel.rom
*.o
apple/applemmu-rom.h
apple/diskii-rom.h
apple/parallel-rom.h
apple/parallel-rom.h
aiie-sdl
mockingboard-d.rom

1666
audio/AY8910.c Normal file

File diff suppressed because it is too large Load Diff

129
audio/AY8910.h Normal file
View File

@ -0,0 +1,129 @@
/*
* Apple // emulator for *ix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2013-2015 Aaron Culliney
*
*/
#ifndef AY8910_H
#define AY8910_H
#define MAX_8910 4
//-------------------------------------
// MAME interface
void _AYWriteReg(int chip, int r, int v
#if MB_TRACING
, FILE *mb_trace_fp
#endif
);
//void AY8910_write_ym(int chip, int addr, int data);
void AY8910_reset(int chip);
void AY8910Update(int chip, int16_t** buffer, int nNumSamples);
void AY8910_InitAll(int nClock, unsigned long nSampleRate);
void AY8910_InitClock(int nClock, unsigned long nSampleRate);
uint8_t* AY8910_GetRegsPtr(unsigned int uChip);
void AY8910UpdateSetCycles();
#if 1 // APPLE2IX
bool _ay8910_saveState(StateHelper_s *helper, unsigned int chip);
bool _ay8910_loadState(StateHelper_s *helper, unsigned int chip);
# if TESTING
int _ay8910_testAssertA2V2(unsigned int chip, uint8_t **exData);
# endif
#else
UINT AY8910_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, UINT uChip, std::string& suffix);
UINT AY8910_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT uChip, std::string& suffix);
#endif
#if MB_TRACING
void _mb_trace_AY8910(int chip, FILE *mb_trace_fp);
#endif
//-------------------------------------
// FUSE stuff
typedef unsigned long libspectrum_dword;
typedef uint8_t libspectrum_byte;
typedef int16_t libspectrum_signed_word;
struct CAY8910;
/* max. number of sub-frame AY port writes allowed;
* given the number of port writes theoretically possible in a
* 50th I think this should be plenty.
*/
#define AY_CHANGE_MAX 8000
/*
class CAY8910
{
public:
*/
void CAY8910_init(struct CAY8910 *_this);
void sound_ay_init(struct CAY8910 *_this);
//void sound_init(struct CAY8910 *_this, const char *device);
void sound_ay_write(struct CAY8910 *_this, int reg, int val, libspectrum_dword now);
void sound_ay_reset(struct CAY8910 *_this);
void sound_frame(struct CAY8910 *_this);
uint8_t* GetAYRegsPtr(struct CAY8910 *_this);
void SetCLK(double CLK);
#if 1 // APPLE2IX
#else
void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, std::string& suffix);
bool LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, std::string& suffix);
#endif
/*
private:
void init( void );
void sound_end( void );
void sound_ay_overlay( void );
*/
typedef struct ay_change_tag
{
libspectrum_dword tstates;
unsigned short ofs;
unsigned char reg, val;
} ay_change_tag;
/*
private:
*/
typedef struct CAY8910
{
/* foo_subcycles are fixed-point with low 16 bits as fractional part.
* The other bits count as the chip does.
*/
unsigned int ay_tone_tick[3], ay_tone_high[3], ay_noise_tick;
unsigned int ay_tone_subcycles, ay_env_subcycles;
unsigned int ay_env_internal_tick, ay_env_tick;
unsigned int ay_tick_incr;
unsigned int ay_tone_period[3], ay_noise_period, ay_env_period;
//static int beeper_last_subpos[2] = { 0, 0 };
/* Local copy of the AY registers */
libspectrum_byte sound_ay_registers[16];
struct ay_change_tag ay_change[ AY_CHANGE_MAX ];
int ay_change_count;
// statics from sound_ay_overlay()
int rng;
int noise_toggle;
int env_first, env_rev, env_counter;
} CAY8910;
// Vars shared between all AY's
extern double m_fCurrentCLK_AY8910;
#endif

19620
audio/SSI263Phonemes.h Normal file

File diff suppressed because it is too large Load Diff

84
audio/alhelpers.c Normal file
View File

@ -0,0 +1,84 @@
/*
* OpenAL Helpers
*
* Copyright (c) 2011 by Chris Robinson <chris.kcat@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* This file contains routines to help with some menial OpenAL-related tasks,
* such as opening a device and setting up a context, closing the device and
* destroying its context, converting between frame counts and byte lengths,
* finding an appropriate buffer format, and getting readable strings for
* channel configs and sample types. */
#include "common.h"
#include "audio/alhelpers.h"
/* InitAL opens the default device and sets up a context using default
* attributes, making the program ready to call OpenAL functions. */
ALCcontext* InitAL(void)
{
ALCdevice *device;
ALCcontext *ctx;
/* Open and initialize a device with default settings */
device = alcOpenDevice(NULL);
if(!device)
{
ERRLOG("Could not open a device!");
return NULL;
}
ctx = alcCreateContext(device, NULL);
if(ctx == NULL || alcMakeContextCurrent(ctx) == ALC_FALSE)
{
if(ctx != NULL)
{
alcDestroyContext(ctx);
}
alcCloseDevice(device);
ERRLOG("Could not set a context!");
return NULL;
}
LOG("Opened \"%s\"", alcGetString(device, ALC_DEVICE_SPECIFIER));
return ctx;
}
/* CloseAL closes the device belonging to the current context, and destroys the
* context. */
void CloseAL(void)
{
ALCdevice *device;
ALCcontext *ctx;
ctx = alcGetCurrentContext();
if(ctx == NULL)
{
return;
}
device = alcGetContextsDevice(ctx);
alcMakeContextCurrent(NULL);
alcDestroyContext(ctx);
alcCloseDevice(device);
}

32
audio/alhelpers.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef ALHELPERS_H
#define ALHELPERS_H
#ifdef __APPLE__
# import <OpenAL/al.h>
# import <OpenAL/alc.h>
#else
# include <AL/al.h>
# include <AL/alc.h>
# include <AL/alext.h>
#endif
#ifndef _WIN32
#include <unistd.h>
#else
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Easy device init/deinit functions. */
ALCcontext* InitAL(void);
void CloseAL(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* ALHELPERS_H */

3382
audio/mockingboard.c Normal file

File diff suppressed because it is too large Load Diff

146
audio/mockingboard.h Normal file
View File

@ -0,0 +1,146 @@
/*
* Apple // emulator for *ix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2013-2015 Aaron Culliney
*
*/
#ifndef _MOCKINGBOARD_H_
#define _MOCKINGBOARD_H_
#ifdef APPLE2IX
#include "audio/peripherals.h"
extern bool g_bDisableDirectSoundMockingboard;
typedef struct
{
union
{
struct
{
uint8_t l;
uint8_t h;
};
uint16_t w;
};
} IWORD;
typedef struct
{
uint8_t ORB; // $00 - Port B
uint8_t ORA; // $01 - Port A (with handshaking)
uint8_t DDRB; // $02 - Data Direction Register B
uint8_t DDRA; // $03 - Data Direction Register A
//
// $04 - Read counter (L) / Write latch (L)
// $05 - Read / Write & initiate count (H)
// $06 - Read / Write & latch (L)
// $07 - Read / Write & latch (H)
// $08 - Read counter (L) / Write latch (L)
// $09 - Read counter (H) / Write latch (H)
IWORD TIMER1_COUNTER;
IWORD TIMER1_LATCH;
IWORD TIMER2_COUNTER;
IWORD TIMER2_LATCH;
//
uint8_t SERIAL_SHIFT; // $0A
uint8_t ACR; // $0B - Auxiliary Control Register
uint8_t PCR; // $0C - Peripheral Control Register
uint8_t IFR; // $0D - Interrupt Flag Register
uint8_t IER; // $0E - Interrupt Enable Register
uint8_t ORA_NO_HS; // $0F - Port A (without handshaking)
} SY6522;
typedef struct
{
uint8_t DurationPhoneme;
uint8_t Inflection; // I10..I3
uint8_t RateInflection;
uint8_t CtrlArtAmp;
uint8_t FilterFreq;
//
uint8_t CurrentMode; // b7:6=Mode; b0=D7 pin (for IRQ)
} SSI263A;
extern SS_CARDTYPE g_Slot4; // Mockingboard, Z80, Mouse in slot4
extern SS_CARDTYPE g_Slot5; // Mockingboard, Z80 in slot5
#define MB_UNITS_PER_CARD 2
typedef struct
{
SY6522 RegsSY6522;
uint8_t RegsAY8910[16];
SSI263A RegsSSI263;
uint8_t nAYCurrentRegister;
bool bTimer1IrqPending;
bool bTimer2IrqPending;
bool bSpeechIrqPending;
} MB_Unit;
typedef struct
{
SS_CARD_HDR Hdr;
MB_Unit Unit[MB_UNITS_PER_CARD];
} SS_CARD_MOCKINGBOARD;
#endif
extern bool g_bMBTimerIrqActive;
#ifdef _DEBUG
extern uint32_t g_uTimer1IrqCount; // DEBUG
#endif
void MB_Initialize();
void MB_Reinitialize();
void MB_Destroy();
void MB_SetEnabled(bool enabled);
bool MB_ISEnabled(void);
void MB_Reset();
void MB_InitializeIO(char *pCxRomPeripheral, unsigned int uSlot4, unsigned int uSlot5);
void MB_Mute();
void MB_Demute();
void MB_StartOfCpuExecute();
void MB_EndOfVideoFrame();
void MB_UpdateCycles(void);
SS_CARDTYPE MB_GetSoundcardType();
void MB_SetSoundcardType(SS_CARDTYPE NewSoundcardType);
double MB_GetFramePeriod();
bool MB_IsActive();
unsigned long MB_GetVolume();
void MB_SetVolumeZeroToTen(unsigned long goesToTen);
void MB_SetVolume(unsigned long dwVolume, unsigned long dwVolumeMax);
#if 1 // APPLE2IX
bool mb_saveState(StateHelper_s *helper);
bool mb_loadState(StateHelper_s *helper);
# if TESTING
int mb_testAssertA2V2(uint8_t *exData, size_t dataSiz);
# endif
#else
void MB_GetSnapshot_v1(struct SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD dwSlot); // For debugger
int MB_SetSnapshot_v1(const struct SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD dwSlot);
std::string MB_GetSnapshotCardName(void);
void MB_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, const UINT uSlot);
bool MB_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version);
#endif
#if 1 // APPLE2IX
uint8_t mb_read(uint16_t ea);
void mb_io_initialize(unsigned int slot4, unsigned int slot5);
# if MB_TRACING
void mb_traceBegin(const char *trace_file);
void mb_traceFlush(void);
void mb_traceEnd(void);
# endif
#endif
#if UNBREAK_SOON
std::string Phasor_GetSnapshotCardName(void);
void Phasor_SaveSnapshot(class YamlSaveHelper& yamlSaveHelper, const UINT uSlot);
bool Phasor_LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version);
#endif
#endif // whole file

56
audio/peripherals.h Normal file
View File

@ -0,0 +1,56 @@
/*
* Apple // emulator for *ix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2013-2015 Aaron Culliney
*
*/
#ifndef _PERIPHERALS_H_
#define _PERIPHERALS_H_
#include "common.h"
typedef enum eIRQSRC {
IS_6522=0x08, // NOTE : matches IRQ... defines in cpu.h
IS_SPEECH=0x10,
IS_SSC=0x20,
IS_MOUSE=0x40
} eIRQSRC;
typedef enum SS_CARDTYPE
{
CT_Empty = 0,
CT_Disk2, // Apple Disk][
CT_SSC, // Apple Super Serial Card
CT_MockingboardC, // Soundcard
CT_GenericPrinter,
CT_GenericHDD, // Hard disk
CT_GenericClock,
CT_MouseInterface,
CT_Z80,
CT_Phasor, // Soundcard
CT_Echo, // Soundcard
CT_SAM, // Soundcard: Software Automated Mouth
} SS_CARDTYPE;
typedef struct
{
unsigned long dwLength; // Byte length of this unit struct
unsigned long dwVersion;
} SS_UNIT_HDR;
typedef struct
{
SS_UNIT_HDR UnitHdr;
unsigned long dwType; // SS_CARDTYPE
unsigned long dwSlot; // [1..7]
} SS_CARD_HDR;
void CpuIrqAssert(eIRQSRC irq_source);
void CpuIrqDeassert(eIRQSRC irq_source);
#endif

804
audio/playqueue.c Normal file
View File

@ -0,0 +1,804 @@
/*
* Apple // emulator for *ix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2013-2015 Aaron Culliney
*
*/
#include "common.h"
#include "playqueue.h"
typedef struct PQListNode_s {
struct PQListNode_s *next;
struct PQListNode_s *prev;
PlayNode_s node;
} PQListNode_s;
typedef struct PQList_s {
PQListNode_s *availNodes; // ->next only LL
PQListNode_s *queuedNodes; // double LL
PQListNode_s *queuedHead;
} PQList_s;
// ----------------------------------------------------------------------------
static inline bool _iterate_list_and_find_node(INOUT PQListNode_s **listNodePtr, long nodeId) {
while (*listNodePtr) {
if ((*listNodePtr)->node.nodeId == nodeId) {
return true;
}
*listNodePtr = (*listNodePtr)->next;
}
return false;
}
static long playq_enqueue(PlayQueue_s *_this, INOUT PlayNode_s *node) {
long err = 0;
do {
PQList_s *list = (PQList_s *)(_this->_internal);
// detach a node from the available pool
PQListNode_s *listNode = list->availNodes;
if (!listNode) {
ERRLOG("Cannot enqueue: no slots available");
err = -1;
break;
}
list->availNodes = listNode->next;
// exchange data
listNode->node.numBytes = node->numBytes;
listNode->node.bytes = node->bytes;
node->nodeId = listNode->node.nodeId;
// enqueue node
listNode->next = list->queuedNodes;
listNode->prev = NULL;
if (list->queuedNodes) {
list->queuedNodes->prev = listNode;
}
list->queuedNodes = listNode;
// reset head
if (listNode->next == NULL) {
list->queuedHead = listNode;
}
} while (0);
return err;
}
static long playq_dequeue(PlayQueue_s *_this, OUTPARM PlayNode_s *node) {
PQList_s *list = (PQList_s *)(_this->_internal);
if (node) {
*node = (PlayNode_s){ 0 };
}
bool found = (list->queuedHead != NULL);
if (found) {
PQListNode_s *listNode = list->queuedHead;
// detach from queued
list->queuedHead = list->queuedHead->prev;
if (list->queuedHead) {
list->queuedHead->next = NULL;
} else {
list->queuedNodes = NULL;
}
// copy data
if (node) {
*node = listNode->node;
}
// attach to available pool
listNode->prev = NULL;
listNode->next = list->availNodes;
list->availNodes = listNode;
/*listNode->node.nodeId = 0;IMMUTABLE*/
listNode->node.numBytes = 0;
listNode->node.bytes = NULL;
} else {
assert(list->queuedNodes == NULL);
}
return found ? 0 : -1;
}
static long playq_remove(PlayQueue_s *_this, INOUT PlayNode_s *node) {
PQList_s *list = (PQList_s *)(_this->_internal);
PQListNode_s *listNode = list->queuedNodes;
bool found = _iterate_list_and_find_node(&listNode, node->nodeId);
if (found) {
// reset head
if (listNode->next == NULL) {
list->queuedHead = listNode->prev;
}
// detach listNode from queued list ...
if (listNode->prev) {
listNode->prev->next = listNode->next;
if (listNode->next) {
listNode->next->prev = listNode->prev;
}
} else {
list->queuedNodes = listNode->next;
if (listNode->next) {
listNode->next->prev = NULL;
}
}
*node = listNode->node; // copy data
listNode->next = list->availNodes;
listNode->prev = NULL;
list->availNodes = listNode;
/*listNode->node.nodeId = 0;IMMUTABLE*/
listNode->node.numBytes = 0;
listNode->node.bytes = NULL;
}
return found ? 0 : -1;
}
static void playq_drain(PlayQueue_s *_this) {
long err = 0;
do {
err = _this->Dequeue(_this, NULL);
} while (err == 0);
}
static long playq_getHead(PlayQueue_s *_this, OUTPARM PlayNode_s *node) {
long err = 0;
PQList_s *list = (PQList_s *)(_this->_internal);
if (list->queuedHead) {
*node = list->queuedHead->node;
} else {
*node = (PlayNode_s){ 0 };
err = -1;
}
return err;
}
static long playq_get(PlayQueue_s *_this, OUTPARM PlayNode_s *node) {
long err = 0;
PQList_s *list = (PQList_s *)(_this->_internal);
PQListNode_s *listNode = list->queuedNodes;
_iterate_list_and_find_node(&listNode, node->nodeId);
if (listNode) {
*node = listNode->node;
} else {
*node = (PlayNode_s){ 0 };
err = -1;
}
return err;
}
static bool playq_canEnqueue(PlayQueue_s *_this) {
PQList_s *list = (PQList_s *)(_this->_internal);
return (list->availNodes != NULL);
}
// ----------------------------------------------------------------------------
void playq_destroyPlayQueue(INOUT PlayQueue_s **queue) {
if (!(*queue)) {
return;
}
if ((*queue)->Drain) {
(*queue)->Drain(*queue);
}
PQList_s *list = (PQList_s *)((*queue)->_internal);
if (list) {
PQListNode_s *node = list->availNodes;
while (node) {
PQListNode_s *p = node;
node = node->next;
FREE(p);
}
}
FREE(list);
FREE(*queue);
}
PlayQueue_s *playq_createPlayQueue(const long *nodeIdPtr, unsigned long numBuffers) {
PlayQueue_s *playq = NULL;
assert(numBuffers <= MAX_PLAYQ_BUFFERS);
do {
playq = CALLOC(1, sizeof(PlayQueue_s));
if (!playq) {
ERRLOG("no memory");
break;
}
PQList_s *list = CALLOC(1, sizeof(PQList_s));
playq->_internal = list;
if (!list) {
ERRLOG("no memory");
break;
}
bool allocSuccess = true;
for (unsigned long i=0; i<numBuffers; i++) {
PQListNode_s *listNode = CALLOC(1, sizeof(PQListNode_s));
LOG("CREATING PlayNode_s node ID: %ld", nodeIdPtr[i]);
listNode->node.nodeId = nodeIdPtr[i];
if (!listNode) {
ERRLOG("no memory");
allocSuccess = false;
break;
}
listNode->next = list->availNodes;
list->availNodes = listNode;
}
if (!allocSuccess) {
break;
}
playq->Enqueue = &playq_enqueue;
playq->Dequeue = &playq_dequeue;
playq->Remove = &playq_remove;
playq->Drain = &playq_drain;
playq->GetHead = &playq_getHead;
playq->Get = &playq_get;
playq->CanEnqueue = &playq_canEnqueue;
return playq;
} while (0);
if (playq) {
playq_destroyPlayQueue(&playq);
}
return NULL;
}
#define SELF_TEST 0
#if SELF_TEST
bool do_logging = true;
FILE *error_log = NULL;
static void _test_creation(void) {
LOG("begin test");
const unsigned long maxNodes = 8;
long nodeIds[maxNodes];
for (unsigned long i=0; i<maxNodes; i++) {
nodeIds[i] = i+42;
}
PlayQueue_s *pq = playq_createPlayQueue(nodeIds, maxNodes);
assert(pq != NULL);
playq_destroyPlayQueue(&pq);
assert(pq == NULL);
}
static void _test_internal_list_creation_integrity(void) {
LOG("begin test");
const unsigned long maxNodes = 8;
long nodeIds[maxNodes];
for (unsigned long i=0; i<maxNodes; i++) {
nodeIds[i] = i+42;
}
PlayQueue_s *pq = playq_createPlayQueue(nodeIds, maxNodes);
assert(pq != NULL);
assert(pq->_internal != NULL);
PQList_s *list = (PQList_s *)(pq->_internal);
assert(list->availNodes);
assert(list->queuedNodes == NULL);
assert(list->queuedHead == NULL);
PQListNode_s *listNode = list->availNodes;
unsigned int count = 0;
while (listNode) {
listNode = listNode->next;
++count;
}
assert (count == maxNodes);
playq_destroyPlayQueue(&pq);
assert(pq == NULL);
}
static void _test_enqueue_dequeue(void) {
LOG("begin test");
const unsigned long maxNodes = 4;
long nodeIds[maxNodes];
for (unsigned long i=0; i<maxNodes; i++) {
nodeIds[i] = i+42;
}
PlayQueue_s *pq = playq_createPlayQueue(nodeIds, maxNodes);
assert(pq != NULL);
uint8_t *bytesPtr = (uint8_t *)&bytesPtr;
unsigned long numBytes = 42;
PQList_s *list = (PQList_s *)(pq->_internal);
assert(list->availNodes);
assert(list->queuedNodes == NULL);
assert(list->queuedHead == NULL);
long err = 0;
PlayNode_s node0 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node0);
assert(err == 0);
assert(list->queuedNodes);
assert(list->queuedHead);
PlayNode_s node1 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node1);
assert(err == 0);
PlayNode_s node2 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node2);
assert(err == 0);
PlayNode_s node3 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node3);
assert(err == 0);
assert(list->availNodes == NULL);
// test over-enqueue
PlayNode_s node4 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node4);
assert(err != 0 && "this should fail");
// check internal list integrity forward
PQListNode_s *listNode = list->queuedNodes;
assert(listNode->node.nodeId == node3.nodeId);
assert(listNode->node.numBytes == node3.numBytes);
assert(listNode->node.bytes == node3.bytes);
listNode = listNode->next;
assert(listNode->node.nodeId == node2.nodeId);
assert(listNode->node.numBytes == node2.numBytes);
assert(listNode->node.bytes == node2.bytes);
listNode = listNode->next;
assert(listNode->node.nodeId == node1.nodeId);
assert(listNode->node.numBytes == node1.numBytes);
assert(listNode->node.bytes == node1.bytes);
listNode = listNode->next;
assert(listNode == list->queuedHead);
assert(listNode->next == NULL);
assert(listNode->node.nodeId == node0.nodeId);
assert(listNode->node.numBytes == node0.numBytes);
assert(listNode->node.bytes == node0.bytes);
// check internal list integrity backward
listNode = listNode->prev;
PQListNode_s *prevHead1 = listNode;
assert(listNode->node.nodeId == node1.nodeId);
assert(listNode->node.numBytes == node1.numBytes);
assert(listNode->node.bytes == node1.bytes);
listNode = listNode->prev;
PQListNode_s *prevHead2 = listNode;
assert(listNode->node.nodeId == node2.nodeId);
assert(listNode->node.numBytes == node2.numBytes);
assert(listNode->node.bytes == node2.bytes);
listNode = listNode->prev;
PQListNode_s *prevHead3 = listNode;
assert(listNode->node.nodeId == node3.nodeId);
assert(listNode->node.numBytes == node3.numBytes);
assert(listNode->node.bytes == node3.bytes);
assert(listNode == list->queuedNodes);
assert(prevHead3 == list->queuedNodes);
assert(listNode->prev == NULL);
// test one dequeue
PlayNode_s dqNode = { 0 };
err = pq->Dequeue(pq, &dqNode);
assert(err == 0);
assert(dqNode.nodeId == node0.nodeId);
assert(dqNode.numBytes == node0.numBytes);
assert(dqNode.bytes == node0.bytes);
assert(list->queuedHead == prevHead1);
assert(list->availNodes != NULL);
assert(list->availNodes->next == NULL);
// test successful re-enqueue of node4
err = pq->Enqueue(pq, &node4);
assert(err == 0);
assert(list->availNodes == NULL);
assert(list->queuedHead == prevHead1);
assert(list->queuedNodes != prevHead3);
// test dequeue all the things ...
err = pq->Dequeue(pq, &dqNode);
assert(err == 0);
assert(dqNode.nodeId == node1.nodeId);
assert(dqNode.numBytes == node1.numBytes);
assert(dqNode.bytes == node1.bytes);
assert(list->queuedHead == prevHead2);
assert(list->availNodes != NULL);
assert(list->availNodes->next == NULL);
err = pq->Dequeue(pq, &dqNode);
assert(err == 0);
assert(dqNode.nodeId == node2.nodeId);
assert(dqNode.numBytes == node2.numBytes);
assert(dqNode.bytes == node2.bytes);
assert(list->queuedHead == prevHead3);
assert(list->availNodes != NULL);
assert(list->availNodes->next != NULL);
assert(list->availNodes->next->next == NULL);
err = pq->Dequeue(pq, &dqNode);
assert(err == 0);
assert(dqNode.nodeId == node3.nodeId);
assert(dqNode.numBytes == node3.numBytes);
assert(dqNode.bytes == node3.bytes);
assert(list->availNodes != NULL);
assert(list->availNodes->next != NULL);
assert(list->availNodes->next->next != NULL);
assert(list->availNodes->next->next->next == NULL);
err = pq->Dequeue(pq, &dqNode);
assert(err == 0);
assert(dqNode.nodeId == node4.nodeId);
assert(dqNode.numBytes == node4.numBytes);
assert(dqNode.bytes == node4.bytes);
assert(list->queuedHead == NULL);
assert(list->queuedNodes == NULL);
assert(list->availNodes != NULL);
assert(list->availNodes->next != NULL);
assert(list->availNodes->next->next != NULL);
assert(list->availNodes->next->next->next != NULL);
assert(list->availNodes->next->next->next->next == NULL);
// test over-dequeue
err = pq->Dequeue(pq, &dqNode);
assert (err != 0 && "cannot dequeue with nothing there");
assert(list->queuedNodes == NULL);
assert(list->queuedHead == NULL);
// cleanup
playq_destroyPlayQueue(&pq);
assert(pq == NULL);
}
static void _test_remove_head_of_queue(void) {
LOG("begin test");
const unsigned long maxNodes = 4;
long nodeIds[maxNodes];
for (unsigned long i=0; i<maxNodes; i++) {
nodeIds[i] = i+42;
}
PlayQueue_s *pq = playq_createPlayQueue(nodeIds, maxNodes);
assert(pq != NULL);
uint8_t *bytesPtr = (uint8_t *)&bytesPtr;
unsigned long numBytes = 42;
long err = 0;
PQList_s *list = (PQList_s *)(pq->_internal);
assert(list->availNodes != NULL);
assert(list->queuedNodes == NULL);
assert(list->queuedHead == NULL);
PlayNode_s node0 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node0);
assert(err == 0);
assert(list->queuedNodes != NULL);
assert(list->queuedHead != NULL);
PQListNode_s *listHead0 = list->queuedHead;
PlayNode_s node1 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node1);
assert(err == 0);
PlayNode_s node2 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node2);
assert(err == 0);
PlayNode_s node3 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node3);
assert(err == 0);
assert(list->queuedHead == listHead0);
// dequeue head node using remove
assert(list->availNodes == NULL);
PlayNode_s dqNode = {
.nodeId = node0.nodeId,
};
err = pq->Remove(pq, &dqNode);
assert(dqNode.nodeId == node0.nodeId);
assert(dqNode.numBytes == node0.numBytes);
assert(dqNode.bytes == node0.bytes);
// check integrity of inner list
assert(list->availNodes != NULL);
assert(list->availNodes == listHead0);
assert(list->availNodes->next == NULL);
assert(list->availNodes->prev == NULL);
assert(list->queuedNodes != NULL);
assert(list->queuedHead != NULL);
PQListNode_s *listNode = list->queuedNodes;
assert(listNode->prev == NULL);
assert(listNode->next->prev == listNode);
listNode = listNode->next;
assert(listNode->next->prev == listNode);
listNode = listNode->next;
assert(listNode->next == NULL);
assert(list->queuedHead == listNode);
// cleanup
playq_destroyPlayQueue(&pq);
assert(pq == NULL);
}
static void _test_remove_tail_of_queue(void) {
LOG("begin test");
const unsigned long maxNodes = 4;
long nodeIds[maxNodes];
for (unsigned long i=0; i<maxNodes; i++) {
nodeIds[i] = i+42;
}
PlayQueue_s *pq = playq_createPlayQueue(nodeIds, maxNodes);
assert(pq != NULL);
uint8_t *bytesPtr = (uint8_t *)&bytesPtr;
unsigned long numBytes = 42;
long err = 0;
PQList_s *list = (PQList_s *)(pq->_internal);
assert(list->availNodes != NULL);
assert(list->queuedNodes == NULL);
assert(list->queuedHead == NULL);
PlayNode_s node0 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node0);
assert(err == 0);
assert(list->queuedNodes != NULL);
assert(list->queuedHead != NULL);
PQListNode_s *listHead0 = list->queuedHead;
PlayNode_s node1 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node1);
assert(err == 0);
PlayNode_s node2 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node2);
assert(err == 0);
PlayNode_s node3 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node3);
assert(err == 0);
PQListNode_s *listHead3 = list->queuedNodes;
assert(list->queuedHead == listHead0);
// dequeue head node using remove
assert(list->availNodes == NULL);
PlayNode_s dqNode = {
.nodeId = node3.nodeId,
};
err = pq->Remove(pq, &dqNode);
assert(dqNode.nodeId == node3.nodeId);
assert(dqNode.numBytes == node3.numBytes);
assert(dqNode.bytes == node3.bytes);
// check integrity of inner list
assert(list->availNodes != NULL);
assert(list->availNodes == listHead3);
assert(list->availNodes->prev == NULL);
assert(list->availNodes->next == NULL);
assert(list->queuedNodes != NULL);
assert(list->queuedHead != NULL);
assert(list->queuedHead == listHead0);
PQListNode_s *listNode = list->queuedNodes;
assert(listNode->prev == NULL);
assert(listNode->next->prev == listNode);
listNode = listNode->next;
assert(listNode->next->prev == listNode);
listNode = listNode->next;
assert(listNode->next == NULL);
assert(list->queuedHead == listNode);
// cleanup
playq_destroyPlayQueue(&pq);
assert(pq == NULL);
}
static void _test_remove_middle_of_queue(void) {
LOG("begin test");
const unsigned long maxNodes = 4;
long nodeIds[maxNodes];
for (unsigned long i=0; i<maxNodes; i++) {
nodeIds[i] = i+42;
}
PlayQueue_s *pq = playq_createPlayQueue(nodeIds, maxNodes);
assert(pq != NULL);
uint8_t *bytesPtr = (uint8_t *)&bytesPtr;
unsigned long numBytes = 42;
long err = 0;
PQList_s *list = (PQList_s *)(pq->_internal);
assert(list->availNodes != NULL);
assert(list->queuedNodes == NULL);
assert(list->queuedHead == NULL);
PlayNode_s node0 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node0);
assert(err == 0);
assert(list->queuedNodes != NULL);
assert(list->queuedHead != NULL);
PQListNode_s *listHead0 = list->queuedHead;
PlayNode_s node1 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node1);
assert(err == 0);
PlayNode_s node2 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node2);
assert(err == 0);
PQListNode_s *listHead2 = list->queuedNodes;
PlayNode_s node3 = {
.numBytes = numBytes++,
.bytes = bytesPtr++,
};
err = pq->Enqueue(pq, &node3);
assert(err == 0);
assert(list->queuedHead == listHead0);
// dequeue head node using remove
assert(list->availNodes == NULL);
PlayNode_s dqNode = {
.nodeId = node2.nodeId,
};
err = pq->Remove(pq, &dqNode);
assert(dqNode.nodeId == node2.nodeId);
assert(dqNode.numBytes == node2.numBytes);
assert(dqNode.bytes == node2.bytes);
// check integrity of inner list
assert(list->availNodes != NULL);
assert(list->availNodes == listHead2);
assert(list->availNodes->prev == NULL);
assert(list->availNodes->next == NULL);
assert(list->queuedNodes != NULL);
assert(list->queuedHead != NULL);
assert(list->queuedHead == listHead0);
PQListNode_s *listNode = list->queuedNodes;
assert(listNode->prev == NULL);
assert(listNode->next->prev == listNode);
listNode = listNode->next;
assert(listNode->next->prev == listNode);
listNode = listNode->next;
assert(listNode->next == NULL);
assert(list->queuedHead == listNode);
// cleanup
playq_destroyPlayQueue(&pq);
assert(pq == NULL);
}
int main(int argc, char **argv) {
#warning use Valgrind to check proper memory management
error_log = stdout;
LOG("beginning tests");
_test_creation();
_test_internal_list_creation_integrity();
_test_enqueue_dequeue();
_test_remove_head_of_queue();
_test_remove_tail_of_queue();
_test_remove_middle_of_queue();
LOG("all tests successful");
return 0;
}
#endif

63
audio/playqueue.h Normal file
View File

@ -0,0 +1,63 @@
/*
* Apple // emulator for *ix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2013-2015 Aaron Culliney
*
*/
/*
* A simple audio buffer play queue.
*
* WARNING : non-thread-safe API ... locking is callee's responsibility (if needed)
*
*/
#ifndef _PLAYQUEUE_H_
#define _PLAYQUEUE_H_
#define MAX_PLAYQ_BUFFERS 16
#define INVALID_NODE_ID INT_MIN
typedef struct PlayNode_s {
long nodeId;
unsigned long numBytes;
uint8_t *bytes;
} PlayNode_s;
typedef struct PlayQueue_s {
PRIVATE void *_internal;
// enqueues a node (IN : numBytes, bytes OUT : nodeId)
long (*Enqueue)(struct PlayQueue_s *_this, INOUT PlayNode_s *node);
// dequeues the head of the queue (OUT : full PlayNode_s data if param is non-null)
long (*Dequeue)(struct PlayQueue_s *_this, OUTPARM PlayNode_s *node);
// finds and removes a specific node (IN : nodeId OUT : full PlayNode_s data)
long (*Remove)(struct PlayQueue_s *_this, INOUT PlayNode_s *node);
// removes all nodes from the queue
void (*Drain)(struct PlayQueue_s *_this);
// gets the head node (OUT : full PlayNode_s data)
long (*GetHead)(struct PlayQueue_s *_this, OUTPARM PlayNode_s *node);
// gets a reference to a specific node (IN : nodeId OUT : full PlayNode_s data)
long (*Get)(struct PlayQueue_s *_this, INOUT PlayNode_s *node);
// true if we can enqueue moar data
bool (*CanEnqueue)(struct PlayQueue_s *_this);
} PlayQueue_s;
// create a play queue object
PlayQueue_s *playq_createPlayQueue(const long *nodeIdPtr, unsigned long numBuffers);
// destroy a play queue object
void playq_destroyPlayQueue(INOUT PlayQueue_s **queue);
#endif /* whole file */

642
audio/soundcore-openal.c Normal file
View File

@ -0,0 +1,642 @@
/*
* Apple // emulator for *ix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2013-2015 Aaron Culliney
*
*/
// soundcore OpenAL backend -- streaming audio
#include "common.h"
#ifdef __APPLE__
# import <OpenAL/al.h>
# import <OpenAL/alc.h>
#else
# include <AL/al.h>
# include <AL/alc.h>
# include <AL/alext.h>
#endif
#include "audio/alhelpers.h"
#include "playqueue.h"
#include "uthash.h"
#define DEBUG_OPENAL 0
#if DEBUG_OPENAL
# define OPENAL_LOG(...) LOG(__VA_ARGS__)
#else
# define OPENAL_LOG(...)
#endif
#define OPENAL_NUM_BUFFERS 4
typedef struct ALVoice {
ALuint source;
ALuint buffers[OPENAL_NUM_BUFFERS];
// playing data
PlayQueue_s *playq;
ALint _queued_total_bytes; // a maximum estimate -- actual value depends on OpenAL query
// working data buffer
ALbyte *data;
ALsizei index; // working buffer byte index
ALsizei buffersize; // working buffer size (and OpenAL buffersize)
ALsizei replay_index;
// sample parameters
ALenum format;
ALenum channels;
ALenum type;
ALuint rate;
} ALVoice;
typedef struct ALVoices {
const ALuint source;
ALVoice *voice;
UT_hash_handle hh;
} ALVoices;
static ALVoices *voices = NULL;
static AudioBackend_s openal_audio_backend = { { 0 } };
// ----------------------------------------------------------------------------
// AudioBuffer_s processing routines
static void _playq_removeNode(ALVoice *voice, PlayNode_s *playNode) {
long err = voice->playq->Remove(voice->playq, playNode);
assert(err == 0);
voice->_queued_total_bytes -= playNode->numBytes;
assert(voice->_queued_total_bytes >= 0);
}
static long _ALProcessPlayBuffers(ALVoice *voice, ALuint *bytes_queued) {
long err = 0;
*bytes_queued = 0;
do {
ALint processed = 0;
alGetSourcei(voice->source, AL_BUFFERS_PROCESSED, &processed);
if ((err = alGetError()) != AL_NO_ERROR) {
ERRLOG("OOPS, error in checking processed buffers : 0x%08lx", err);
break;
}
while (processed > 0) {
--processed;
ALuint bufid = 0;
alSourceUnqueueBuffers(voice->source, 1, &bufid);
if ((err = alGetError()) != AL_NO_ERROR) {
ERRLOG("OOPS, OpenAL error dequeuing buffer : 0x%08lx", err);
break;
}
OPENAL_LOG("Attempting to dequeue %u ...", bufid);
PlayNode_s playNode = {
.nodeId = bufid,
};
err = voice->playq->Get(voice->playq, &playNode);
if (err) {
ERRLOG("OOPS, OpenAL bufid %u not found in playlist...", bufid);
} else {
_playq_removeNode(voice, &playNode);
}
}
ALint play_offset = 0;
alGetSourcei(voice->source, AL_BYTE_OFFSET, &play_offset);
if ((err = alGetError()) != AL_NO_ERROR) {
ERRLOG("OOPS, alGetSourcei AL_BYTE_OFFSET : 0x%08lx", err);
break;
}
assert((play_offset >= 0)/* && (play_offset < voice->buffersize)*/);
long q = voice->_queued_total_bytes/* + voice->index*/ - play_offset;
if (q >= 0) {
*bytes_queued = (ALuint)q;
}
} while (0);
return err;
}
// returns queued+working sound buffer size in bytes
static long ALGetPosition(AudioBuffer_s *_this, OUTPARM unsigned long *bytes_queued) {
*bytes_queued = 0;
long err = 0;
do {
ALVoice *voice = (ALVoice*)_this->_internal;
ALuint queued = 0;
long err = _ALProcessPlayBuffers(voice, &queued);
if (err) {
break;
}
#if DEBUG_OPENAL
static int last_queued = 0;
if (queued != last_queued) {
last_queued = queued;
OPENAL_LOG("OpenAL bytes queued : %u", queued);
}
#endif
*bytes_queued = queued + voice->index;
} while (0);
return err;
}
static long ALLockBuffer(AudioBuffer_s *_this, unsigned long write_bytes, INOUT int16_t **audio_ptr, OUTPARM unsigned long *audio_bytes) {
*audio_bytes = 0;
*audio_ptr = NULL;
long err = 0;
do {
ALVoice *voice = (ALVoice*)_this->_internal;
if (write_bytes == 0) {
write_bytes = voice->buffersize;
}
ALuint bytes_queued = 0;
err = _ALProcessPlayBuffers(voice, &bytes_queued);
if (err) {
break;
}
if ((bytes_queued == 0) && (voice->index == 0)) {
LOG("Buffer underrun ... queuing quiet samples ...");
int quiet_size = voice->buffersize>>2/* 1/4 buffer */;
memset(voice->data, 0x0, quiet_size);
voice->index += quiet_size;
}
#if 0
else if (bytes_queued + voice->index < (voice->buffersize>>3)/* 1/8 buffer */)
{
OPENAL_LOG("Potential underrun ...");
}
#endif
ALsizei remaining = voice->buffersize - voice->index;
if (write_bytes > remaining) {
write_bytes = remaining;
}
*audio_ptr = (int16_t *)(voice->data+voice->index);
*audio_bytes = write_bytes;
} while (0);
return err;
}
static long _ALSubmitBufferToOpenAL(ALVoice *voice) {
long err = 0;
do {
// Micro-manage play queue locally to understand the total bytes-in-play
PlayNode_s playNode = {
.nodeId = INVALID_NODE_ID,
.numBytes = voice->index,
.bytes = (uint8_t *)(voice->data),
};
err = voice->playq->Enqueue(voice->playq, &playNode);
if (err) {
break;
}
voice->_queued_total_bytes += voice->index;
voice->index = 0;
assert(voice->_queued_total_bytes > 0);
OPENAL_LOG("Enqueing OpenAL buffer %ld (%lu bytes) at %p", playNode.nodeId, playNode.numBytes, playNode.bytes);
alBufferData(playNode.nodeId, voice->format, playNode.bytes, playNode.numBytes, voice->rate);
if ((err = alGetError()) != AL_NO_ERROR) {
_playq_removeNode(voice, &playNode);
ERRLOG("OOPS, Error alBufferData : 0x%08lx", err);
break;
}
ALuint nodeId = (ALuint)playNode.nodeId;
alSourceQueueBuffers(voice->source, 1, &nodeId);
if ((err = alGetError()) != AL_NO_ERROR) {
_playq_removeNode(voice, &playNode);
ERRLOG("OOPS, Error buffering data : 0x%08lx", err);
break;
}
ALint state = 0;
alGetSourcei(voice->source, AL_SOURCE_STATE, &state);
if ((err = alGetError()) != AL_NO_ERROR) {
ERRLOG("OOPS, Error checking source state : 0x%08lx", err);
break;
}
if ((state != AL_PLAYING) && (state != AL_PAUSED)) {
// 2013/11/17 NOTE : alSourcePlay() is expensive and causes audio artifacts, only invoke if needed
LOG("Restarting playback (was 0x%08x) ...", state);
alSourcePlay(voice->source);
if ((err = alGetError()) != AL_NO_ERROR) {
LOG("Error starting playback : 0x%08lx", err);
break;
}
}
} while (0);
return err;
}
static long ALUnlockBuffer(AudioBuffer_s *_this, unsigned long audio_bytes) {
long err = 0;
do {
ALVoice *voice = (ALVoice*)_this->_internal;
ALuint bytes_queued = 0;
err = _ALProcessPlayBuffers(voice, &bytes_queued);
if (err) {
break;
}
voice->index += audio_bytes;
if (voice->index >= voice->buffersize) {
assert((voice->index == voice->buffersize) && "OOPS, detected an actual overflow in queued sound data");
}
if (bytes_queued >= (voice->buffersize>>2)/*quarter buffersize*/) {
// keep accumulating data into working buffer
break;
}
if (! (voice->playq->CanEnqueue(voice->playq)) ) {
OPENAL_LOG("no free audio buffers"); // keep accumulating ...
break;
}
// Submit working buffer to OpenAL
err = _ALSubmitBufferToOpenAL(voice);
} while (0);
return err;
}
#if 0
// HACK Part I : done once for mockingboard that has semiauto repeating phonemes ...
static long ALUnlockStaticBuffer(AudioBuffer_s *_this, unsigned long audio_bytes) {
ALVoice *voice = (ALVoice*)_this->_internal;
voice->replay_index = (ALsizei)audio_bytes;
return 0;
}
// HACK Part II : replay mockingboard phoneme ...
static long ALReplay(AudioBuffer_s *_this) {
ALVoice *voice = (ALVoice*)_this->_internal;
voice->index = voice->replay_index;
long err = _ALSubmitBufferToOpenAL(voice);
return err;
}
#endif
static long ALGetStatus(AudioBuffer_s *_this, OUTPARM unsigned long *status) {
*status = -1;
long err = 0;
do {
ALVoice* voice = (ALVoice*)_this->_internal;
ALint state = 0;
alGetSourcei(voice->source, AL_SOURCE_STATE, &state);
if ((err = alGetError()) != AL_NO_ERROR) {
ERRLOG("OOPS, Error checking source state : 0x%08lx", err);
break;
}
if ((state == AL_PLAYING) || (state == AL_PAUSED)) {
*status = AUDIO_STATUS_PLAYING;
} else {
*status = AUDIO_STATUS_NOTPLAYING;
}
} while (0);
return err;
}
// ----------------------------------------------------------------------------
// ALVoice is the AudioBuffer_s->_internal
static void _openal_destroyVoice(ALVoice *voice) {
alDeleteSources(1, &voice->source);
if (alGetError() != AL_NO_ERROR) {
ERRLOG("OOPS, Failed to delete source");
}
if (voice->data) {
FREE(voice->data);
}
for (unsigned int i=0; i<OPENAL_NUM_BUFFERS; i++) {
alDeleteBuffers(1, voice->buffers);
if (alGetError() != AL_NO_ERROR) {
ERRLOG("OOPS, Failed to delete object IDs");
}
}
playq_destroyPlayQueue(&(voice->playq));
memset(voice, 0, sizeof(*voice));
FREE(voice);
}
static ALVoice *_openal_createVoice(unsigned long numChannels) {
ALVoice *voice = NULL;
do {
voice = CALLOC(1, sizeof(*voice));
if (voice == NULL) {
ERRLOG("OOPS, Out of memory!");
break;
}
alGenBuffers(OPENAL_NUM_BUFFERS, voice->buffers);
if (alGetError() != AL_NO_ERROR) {
ERRLOG("OOPS, Could not create buffers");
break;
}
alGenSources(1, &voice->source);
if (alGetError() != AL_NO_ERROR) {
ERRLOG("OOPS, Could not create source");
break;
}
// Set parameters so mono sources play out the front-center speaker and won't distance attenuate.
alSource3i(voice->source, AL_POSITION, 0, 0, -1);
if (alGetError() != AL_NO_ERROR) {
ERRLOG("OOPS, Could not set AL_POSITION source parameter");
break;
}
alSourcei(voice->source, AL_SOURCE_RELATIVE, AL_TRUE);
if (alGetError() != AL_NO_ERROR) {
ERRLOG("OOPS, Could not set AL_SOURCE_RELATIVE source parameter");
break;
}
alSourcei(voice->source, AL_ROLLOFF_FACTOR, 0);
if (alGetError() != AL_NO_ERROR) {
ERRLOG("OOPS, Could not set AL_ROLLOFF_FACTOR source parameter");
break;
}
#if 0
alSourcei(voice->source, AL_STREAMING, AL_TRUE);
if (alGetError() != AL_NO_ERROR) {
ERRLOG("OOPS, Could not set AL_STREAMING source parameter");
break;
}
#endif
long longBuffers[OPENAL_NUM_BUFFERS];
for (unsigned int i=0; i<OPENAL_NUM_BUFFERS; i++) {
longBuffers[i] = (long)(voice->buffers[i]);
}
voice->playq = playq_createPlayQueue(longBuffers, OPENAL_NUM_BUFFERS);
if (!voice->playq) {
ERRLOG("OOPS, Not enough memory for PlayQueue");
break;
}
voice->rate = openal_audio_backend.systemSettings.sampleRateHz;
// Emulator supports only mono and stereo
if (numChannels == 2) {
voice->format = AL_FORMAT_STEREO16;
} else {
voice->format = AL_FORMAT_MONO16;
}
/* Allocate enough space for the temp buffer, given the format */
assert(numChannels == 1 || numChannels == 2);
unsigned long maxSamples = openal_audio_backend.systemSettings.monoBufferSizeSamples * numChannels;
voice->buffersize = maxSamples * openal_audio_backend.systemSettings.bytesPerSample;
voice->data = CALLOC(1, voice->buffersize);
if (voice->data == NULL) {
ERRLOG("OOPS, Error allocating %d bytes", voice->buffersize);
break;
}
LOG("\tRate : 0x%08x", voice->rate);
LOG("\tFormat : 0x%08x", voice->format);
LOG("\tbuffersize : %d", voice->buffersize);
return voice;
} while(0);
// ERR
if (voice) {
_openal_destroyVoice(voice);
}
return NULL;
}
// ----------------------------------------------------------------------------
static long openal_destroySoundBuffer(const struct AudioContext_s *sound_system, INOUT AudioBuffer_s **soundbuf_struct) {
if (!*soundbuf_struct) {
// already dealloced
return 0;
}
LOG("openal_destroySoundBuffer ...");
ALVoice *voice = (ALVoice *)((*soundbuf_struct)->_internal);
ALint source = voice->source;
_openal_destroyVoice(voice);
ALVoices *vnode = NULL;
HASH_FIND_INT(voices, &source, vnode);
if (vnode) {
HASH_DEL(voices, vnode);
FREE(vnode);
}
FREE(*soundbuf_struct);
return 0;
}
static long openal_createSoundBuffer(const AudioContext_s *audio_context, INOUT AudioBuffer_s **soundbuf_struct) {
LOG("openal_createSoundBuffer ...");
assert(*soundbuf_struct == NULL);
ALVoice *voice = NULL;
do {
ALCcontext *ctx = (ALCcontext*)(audio_context->_internal);
assert(ctx != NULL);
if ((voice = _openal_createVoice(NUM_CHANNELS)) == NULL) {
ERRLOG("OOPS, Cannot create new voice");
break;
}
ALVoices immutableNode = { /*const*/.source = voice->source };
ALVoices *vnode = CALLOC(1, sizeof(ALVoices));
if (!vnode) {
ERRLOG("OOPS, Not enough memory");
break;
}
memcpy(vnode, &immutableNode, sizeof(ALVoices));
vnode->voice = voice;
HASH_ADD_INT(voices, source, vnode);
if ((*soundbuf_struct = CALLOC(1, sizeof(AudioBuffer_s))) == NULL) {
ERRLOG("OOPS, Not enough memory");
break;
}
(*soundbuf_struct)->_internal = voice;
(*soundbuf_struct)->GetCurrentPosition = &ALGetPosition;
(*soundbuf_struct)->Lock = &ALLockBuffer;
(*soundbuf_struct)->Unlock = &ALUnlockBuffer;
(*soundbuf_struct)->GetStatus = &ALGetStatus;
// mockingboard-specific hacks
//(*soundbuf_struct)->UnlockStaticBuffer = &ALUnlockStaticBuffer;
//(*soundbuf_struct)->Replay = &ALReplay;
return 0;
} while(0);
if (*soundbuf_struct) {
openal_destroySoundBuffer(audio_context, soundbuf_struct);
} else if (voice) {
_openal_destroyVoice(voice);
}
return -1;
}
// ----------------------------------------------------------------------------
static long openal_systemShutdown(INOUT AudioContext_s **audio_context) {
assert(*audio_context != NULL);
ALCcontext *ctx = (ALCcontext*) (*audio_context)->_internal;
assert(ctx != NULL);
(*audio_context)->_internal = NULL;
FREE(*audio_context);
// NOTE : currently assuming just one OpenAL global context
CloseAL();
return 0;
}
static long openal_systemSetup(INOUT AudioContext_s **audio_context) {
assert(*audio_context == NULL);
assert(voices == NULL);
long result = -1;
ALCcontext *ctx = NULL;
// 2015/06/29 these values seem to work well on Linux desktop ... no other OpenAL platform has been tested
openal_audio_backend.systemSettings.sampleRateHz = 22050;
openal_audio_backend.systemSettings.bytesPerSample = 2;
openal_audio_backend.systemSettings.monoBufferSizeSamples = (8*1024);
openal_audio_backend.systemSettings.stereoBufferSizeSamples = openal_audio_backend.systemSettings.monoBufferSizeSamples;
do {
if ((ctx = InitAL()) == NULL) {
// NOTE : currently assuming just one OpenAL global context
ERRLOG("OOPS, OpenAL initialize failed");
break;
}
if (alIsExtensionPresent("AL_SOFT_buffer_samples")) {
LOG("AL_SOFT_buffer_samples supported, good!");
} else {
LOG("WARNING - AL_SOFT_buffer_samples extension not supported... Proceeding anyway...");
}
if ((*audio_context = CALLOC(1, sizeof(AudioContext_s))) == NULL) {
ERRLOG("OOPS, Not enough memory");
break;
}
(*audio_context)->_internal = ctx;
(*audio_context)->CreateSoundBuffer = &openal_createSoundBuffer;
(*audio_context)->DestroySoundBuffer = &openal_destroySoundBuffer;
result = 0;
} while(0);
if (result) {
if (ctx) {
AudioContext_s *ctxPtr = CALLOC(1, sizeof(AudioContext_s));
ctxPtr->_internal = ctx;
openal_systemShutdown(&ctxPtr);
}
assert (*audio_context == NULL);
LOG("OpenAL sound output disabled");
}
return result;
}
static long openal_systemPause(AudioContext_s *audio_context) {
ALVoices *vnode = NULL;
ALVoices *tmp = NULL;
long err = 0;
HASH_ITER(hh, voices, vnode, tmp) {
alSourcePause(vnode->source);
err = alGetError();
if (err != AL_NO_ERROR) {
ERRLOG("OOPS, Failed to pause source : 0x%08lx", err);
}
}
return 0;
}
static long openal_systemResume(AudioContext_s *audio_context) {
ALVoices *vnode = NULL;
ALVoices *tmp = NULL;
long err = 0;
HASH_ITER(hh, voices, vnode, tmp) {
alSourcePlay(vnode->source);
err = alGetError();
if (err != AL_NO_ERROR) {
ERRLOG("OOPS, Failed to pause source : 0x%08lx", err);
}
}
return 0;
}
static void _init_openal(void) {
LOG("Initializing OpenAL sound system");
assert((audio_backend == NULL) && "there can only be one!");
openal_audio_backend.setup = &openal_systemSetup;
openal_audio_backend.shutdown = &openal_systemShutdown;
openal_audio_backend.pause = &openal_systemPause;
openal_audio_backend.resume = &openal_systemResume;
audio_backend = &openal_audio_backend;
}
static __attribute__((constructor)) void __init_openal(void) {
emulator_registerStartupCallback(CTOR_PRIORITY_EARLY, &_init_openal);
}

777
audio/soundcore-opensles.c Normal file
View File

@ -0,0 +1,777 @@
/*
* Apple // emulator for *ix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2013-2015 Aaron Culliney
*
*/
// soundcore OpenSLES backend -- streaming audio
#include "common.h"
#include <SLES/OpenSLES.h>
#if defined(ANDROID)
# include <SLES/OpenSLES_Android.h>
#else
# error FIXME TODO this currently uses Android BufferQueue extensions...
#endif
#define DEBUG_OPENSL 0
#if DEBUG_OPENSL
# define OPENSL_LOG(...) LOG(__VA_ARGS__)
#else
# define OPENSL_LOG(...)
#endif
#define NUM_CHANNELS 2
typedef struct SLVoice {
void *ctx; // EngineContext_s
// working data buffer
uint8_t *ringBuffer; // ringBuffer of total size : bufferSize+submitSize
unsigned long bufferSize; // ringBuffer non-overflow size
ptrdiff_t writeHead; // head of the writer of ringBuffer (speaker, mockingboard)
unsigned long writeWrapCount; // count of buffer wraps for the writer
unsigned long spinLock; // spinlock around reader variables
ptrdiff_t readHead; // head of the reader of ringBuffer (OpenSLES callback)
unsigned long readWrapCount; // count of buffer wraps for the reader
// next voice
struct SLVoice *next;
} SLVoice;
typedef struct EngineContext_s {
SLObjectItf engineObject;
SLEngineItf engineEngine;
SLObjectItf outputMixObject;
SLObjectItf bqPlayerObject;
SLPlayItf bqPlayerPlay;
SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
uint8_t *mixBuf; // mix buffer submitted to OpenSLES
unsigned long submitSize; // buffer size OpenSLES expects/wants
SLVoice *voices;
SLVoice *recycledVoices;
} EngineContext_s;
static AudioBackend_s opensles_audio_backend = { 0 };
// ----------------------------------------------------------------------------
// AudioBuffer_s internal processing routines
// Check and resets underrun condition (readHead has advanced beyond writeHead)
static inline bool _underrun_check_and_manage(SLVoice *voice, OUTPARM unsigned long *workingBytes) {
SPINLOCK_ACQUIRE(&voice->spinLock);
unsigned long readHead = voice->readHead;
unsigned long readWrapCount = voice->readWrapCount;
SPINLOCK_RELINQUISH(&voice->spinLock);
assert(readHead < voice->bufferSize);
assert(voice->writeHead < voice->bufferSize);
bool isUnder = false;
if ( (readWrapCount > voice->writeWrapCount) ||
((readHead >= voice->writeHead) && (readWrapCount == voice->writeWrapCount)) )
{
isUnder = true;
LOG("Buffer underrun ...");
voice->writeHead = readHead;
voice->writeWrapCount = readWrapCount;
}
if (readHead <= voice->writeHead) {
*workingBytes = voice->writeHead - readHead;
} else {
*workingBytes = voice->writeHead + (voice->bufferSize - readHead);
}
return isUnder;
}
// This callback handler is called presumably every time (or just prior to when) a buffer finishes playing and the
// system needs moar data (of the correct buffer size)
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
// invariant : can always read submitSize from position of readHead (bufferSize+submitSize)
EngineContext_s *ctx = (EngineContext_s *)context;
SLresult result = SL_RESULT_SUCCESS;
do {
// This is a very simple inline mixer so that we only need one BufferQueue (which works best on low-end Android
// devices
//
// HACK ASSUMPTIONS :
// * max of 2 voices/buffers
// * both buffers contain stereo signed 16bit samples with zero as mid point
// * absolute value of maximum amplitude is less than one half SHRT_MAX (to avoid clipping)
SLVoice *voice0 = ctx->voices;
if (!voice0) {
result = -1;
break;
}
// copy/mix data
memcpy(ctx->mixBuf, voice0->ringBuffer+voice0->readHead, ctx->submitSize);
SLVoice *voice1 = voice0->next;
if (voice1) {
// add second waveform into mix buffer
////if (SIMD_IS_AVAILABLE()) {
#if USE_SIMD
#warning FIXME TODO vectorial code here
#endif
////} else {
uint16_t *mixBuf = (uint16_t *)ctx->mixBuf;
unsigned long submitSize = ctx->submitSize>>1;
for (unsigned long i=0; i<submitSize; i++) {
mixBuf[i] += ((uint16_t *)(voice1->ringBuffer+voice1->readHead))[i];
}
////}
}
// submit data to OpenSLES
result = (*bq)->Enqueue(bq, ctx->mixBuf, ctx->submitSize);
// now manage quiet backfilling and overflow/wrapping ...
memset(voice0->ringBuffer+voice0->readHead, 0x0, ctx->submitSize); // backfill quiet samples
unsigned long newReadHead0 = voice0->readHead + ctx->submitSize;
unsigned long newReadWrapCount0 = voice0->readWrapCount;
if (newReadHead0 >= voice0->bufferSize) {
newReadHead0 = newReadHead0 - voice0->bufferSize;
memset(voice0->ringBuffer+voice0->bufferSize, 0x0, ctx->submitSize); // backfill quiet samples
memset(voice0->ringBuffer, 0x0, newReadHead0);
++newReadWrapCount0;
}
SPINLOCK_ACQUIRE(&voice0->spinLock);
voice0->readHead = newReadHead0;
voice0->readWrapCount = newReadWrapCount0;
SPINLOCK_RELINQUISH(&voice0->spinLock);
if (voice1) {
memset(voice1->ringBuffer+voice1->readHead, 0x0, ctx->submitSize); // backfill quiet samples
unsigned long newReadHead1 = voice1->readHead + ctx->submitSize;
unsigned long newReadWrapCount1 = voice1->readWrapCount;
if (newReadHead1 >= voice1->bufferSize) {
newReadHead1 = newReadHead1 - voice1->bufferSize;
memset(voice1->ringBuffer+voice1->bufferSize, 0x0, ctx->submitSize); // backfill quiet samples
memset(voice1->ringBuffer, 0x0, newReadHead1);
++newReadWrapCount1;
}
SPINLOCK_ACQUIRE(&voice1->spinLock);
voice1->readHead = newReadHead1;
voice1->readWrapCount = newReadWrapCount1;
SPINLOCK_RELINQUISH(&voice1->spinLock);
}
} while (0);
if (result != SL_RESULT_SUCCESS) {
LOG("WARNING: could not enqueue data to OpenSLES!");
(*(ctx->bqPlayerPlay))->SetPlayState(ctx->bqPlayerPlay, SL_PLAYSTATE_STOPPED);
}
}
static long _SLMaybeSubmitAndStart(SLVoice *voice) {
SLuint32 state = 0;
EngineContext_s *ctx = (EngineContext_s *)voice->ctx;
SLresult result = (*(ctx->bqPlayerPlay))->GetPlayState(ctx->bqPlayerPlay, &state);
if (result != SL_RESULT_SUCCESS) {
ERRLOG("OOPS, could not get source state : %lu", (unsigned long)result);
} else {
if ((state != SL_PLAYSTATE_PLAYING) && (state != SL_PLAYSTATE_PAUSED)) {
LOG("FORCING restart audio buffer queue playback ...");
result = (*(ctx->bqPlayerPlay))->SetPlayState(ctx->bqPlayerPlay, SL_PLAYSTATE_PLAYING);
bqPlayerCallback(ctx->bqPlayerBufferQueue, ctx);
}
}
return result;
}
// ----------------------------------------------------------------------------
// AudioBuffer_s public API handlers
// returns queued+working sound buffer size in bytes
static long SLGetPosition(AudioBuffer_s *_this, OUTPARM unsigned long *bytes_queued) {
*bytes_queued = 0;
long err = 0;
do {
SLVoice *voice = (SLVoice*)_this->_internal;
unsigned long workingBytes = 0;
bool underrun = _underrun_check_and_manage(voice, &workingBytes);
//bool overrun = _overrun_check_and_manage(voice);
unsigned long queuedBytes = 0;
if (!underrun) {
//queuedBytes = ctx->submitSize; // assume that there are always about this much actually queued
}
assert(workingBytes <= voice->bufferSize);
*bytes_queued = workingBytes;
} while (0);
return err;
}
static long SLLockBuffer(AudioBuffer_s *_this, unsigned long write_bytes, INOUT int16_t **audio_ptr, OUTPARM unsigned long *audio_bytes) {
*audio_bytes = 0;
*audio_ptr = NULL;
long err = 0;
//OPENSL_LOG("SLLockBuffer request for %ld bytes", write_bytes);
do {
SLVoice *voice = (SLVoice*)_this->_internal;
EngineContext_s *ctx = (EngineContext_s *)voice->ctx;
if (write_bytes == 0) {
LOG("HMMM ... writing full buffer!");
write_bytes = voice->bufferSize;
}
unsigned long workingBytes = 0;
_underrun_check_and_manage(voice, &workingBytes);
unsigned long availableBytes = voice->bufferSize - workingBytes;
assert(workingBytes <= voice->bufferSize);
assert(voice->writeHead < voice->bufferSize);
// TODO FIXME : maybe need to resurrect the 2 inner pointers and foist the responsibility onto the
// speaker/mockingboard modules so we can actually write moar here?
unsigned long writableBytes = MIN( availableBytes, ((voice->bufferSize+ctx->submitSize) - voice->writeHead) );
if (write_bytes > writableBytes) {
OPENSL_LOG("NOTE truncating audio buffer (call again to write complete requested buffer) ...");
write_bytes = writableBytes;
}
*audio_ptr = (int16_t *)(voice->ringBuffer+voice->writeHead);
*audio_bytes = write_bytes;
} while (0);
return err;
}
static long SLUnlockBuffer(AudioBuffer_s *_this, unsigned long audio_bytes) {
long err = 0;
do {
SLVoice *voice = (SLVoice*)_this->_internal;
EngineContext_s *ctx = (EngineContext_s *)voice->ctx;
unsigned long previousWriteHead = voice->writeHead;
voice->writeHead += audio_bytes;
assert((voice->writeHead <= (voice->bufferSize + ctx->submitSize)) && "OOPS, real overflow in queued sound data!");
if (voice->writeHead >= voice->bufferSize) {
// copy data from overflow into beginning of buffer
voice->writeHead = voice->writeHead - voice->bufferSize;
++voice->writeWrapCount;
memcpy(voice->ringBuffer, voice->ringBuffer+voice->bufferSize, voice->writeHead);
} else if (previousWriteHead < ctx->submitSize) {
// copy data in beginning of buffer into overflow position
unsigned long copyNumBytes = MIN(audio_bytes, ctx->submitSize-previousWriteHead);
memcpy(voice->ringBuffer+voice->bufferSize+previousWriteHead, voice->ringBuffer+previousWriteHead, copyNumBytes);
}
err = _SLMaybeSubmitAndStart(voice);
} while (0);
return err;
}
#if 0
// HACK Part I : done once for mockingboard that has semiauto repeating phonemes ...
static long SLUnlockStaticBuffer(AudioBuffer_s *_this, unsigned long audio_bytes) {
SLVoice *voice = (SLVoice*)_this->_internal;
voice->replay_index = audio_bytes;
return 0;
}
// HACK Part II : replay mockingboard phoneme ...
static long SLReplay(AudioBuffer_s *_this) {
SLVoice *voice = (SLVoice*)_this->_internal;
SPINLOCK_ACQUIRE(&voice->spinLock);
voice->readHead = 0;
voice->writeHead = voice->replay_index;
SPINLOCK_RELINQUISH(&voice->spinLock);
long err = _SLMaybeSubmitAndStart(voice);
#warning FIXME TODO ... how do we handle mockingboard for new OpenSLES buffer queue codepath?
return err;
}
#endif
static long SLGetStatus(AudioBuffer_s *_this, OUTPARM unsigned long *status) {
*status = -1;
SLresult result = SL_RESULT_UNKNOWN_ERROR;
do {
SLVoice* voice = (SLVoice*)_this->_internal;
EngineContext_s *ctx = (EngineContext_s *)voice->ctx;
SLuint32 state = 0;
result = (*(ctx->bqPlayerPlay))->GetPlayState(ctx->bqPlayerPlay, &state);
if (result != SL_RESULT_SUCCESS) {
ERRLOG("OOPS, could not get source state : %lu", (unsigned long)result);
break;
}
if ((state == SL_PLAYSTATE_PLAYING) || (state == SL_PLAYSTATE_PAUSED)) {
*status = AUDIO_STATUS_PLAYING;
} else {
*status = AUDIO_STATUS_NOTPLAYING;
}
} while (0);
return (long)result;
}
// ----------------------------------------------------------------------------
// SLVoice is the AudioBuffer_s->_internal
static inline void _opensl_destroyVoice(SLVoice *voice) {
if (voice->ringBuffer) {
FREE(voice->ringBuffer);
}
memset(voice, 0, sizeof(*voice));
FREE(voice);
}
static long opensl_destroySoundBuffer(const struct AudioContext_s *audio_context, INOUT AudioBuffer_s **soundbuf_struct) {
if (!*soundbuf_struct) {
return 0;
}
LOG("opensl_destroySoundBuffer ...");
EngineContext_s *ctx = (EngineContext_s *)(audio_context->_internal);
SLVoice *v = (SLVoice *)((*soundbuf_struct)->_internal);
SLVoice *vprev = NULL;
SLVoice *voice = ctx->voices;
while (voice) {
if (voice == v) {
if (vprev) {
vprev->next = voice->next;
} else {
ctx->voices = voice->next;
}
break;
}
vprev = voice;
voice = voice->next;
}
assert(voice && "voice should exist, or speaker, mockingboard, etc are not using this internal API correctly!");
// Do not actually destory the voice here since we could race with the buffer queue. purge these on complete sound
// system shutdown
voice->next = ctx->recycledVoices;
ctx->recycledVoices = voice;
memset(*soundbuf_struct, 0x0, sizeof(soundbuf_struct));
FREE(*soundbuf_struct);
return 0;
}
static long opensl_createSoundBuffer(const AudioContext_s *audio_context, INOUT AudioBuffer_s **soundbuf_struct) {
LOG("opensl_createSoundBuffer ...");
assert(*soundbuf_struct == NULL);
SLVoice *voice = NULL;
do {
EngineContext_s *ctx = (EngineContext_s *)(audio_context->_internal);
assert(ctx != NULL);
unsigned long bufferSize = opensles_audio_backend.systemSettings.stereoBufferSizeSamples * opensles_audio_backend.systemSettings.bytesPerSample * NUM_CHANNELS;
if (ctx->recycledVoices) {
LOG("Recycling previous SLVoice ...");
voice = ctx->recycledVoices;
ctx->recycledVoices = voice->next;
uint8_t *prevBuffer = voice->ringBuffer;
memset(voice, 0x0, sizeof(*voice));
voice->bufferSize = bufferSize;
voice->ringBuffer = prevBuffer;
} else {
LOG("Creating new SLVoice ...");
voice = CALLOC(1, sizeof(*voice));
if (voice == NULL) {
ERRLOG("OOPS, Out of memory!");
break;
}
voice->bufferSize = bufferSize;
// Allocate enough space for the temp buffer (including a maximum allowed overflow)
voice->ringBuffer = CALLOC(1, voice->bufferSize + ctx->submitSize/*max overflow*/);
if (voice->ringBuffer == NULL) {
ERRLOG("OOPS, Error allocating %lu bytes", (unsigned long)voice->bufferSize+ctx->submitSize);
break;
}
}
LOG("ideal stereo submission bufsize is %lu (bytes:%lu)", (unsigned long)android_stereoBufferSubmitSizeSamples, (unsigned long)ctx->submitSize);
voice->ctx = ctx;
if ((*soundbuf_struct = CALLOC(1, sizeof(AudioBuffer_s))) == NULL) {
ERRLOG("OOPS, Not enough memory");
break;
}
(*soundbuf_struct)->_internal = voice;
(*soundbuf_struct)->GetCurrentPosition = &SLGetPosition;
(*soundbuf_struct)->Lock = &SLLockBuffer;
(*soundbuf_struct)->Unlock = &SLUnlockBuffer;
(*soundbuf_struct)->GetStatus = &SLGetStatus;
// mockingboard-specific (SSI263) hacks
//(*soundbuf_struct)->UnlockStaticBuffer = &SLUnlockStaticBuffer;
//(*soundbuf_struct)->Replay = &SLReplay;
voice->next = ctx->voices;
ctx->voices = voice;
LOG("Successfully created SLVoice");
return 0;
} while(0);
if (*soundbuf_struct) {
opensl_destroySoundBuffer(audio_context, soundbuf_struct);
} else if (voice) {
_opensl_destroyVoice(voice);
}
return -1;
}
// ----------------------------------------------------------------------------
static long opensles_systemShutdown(AudioContext_s **audio_context) {
assert(*audio_context != NULL);
EngineContext_s *ctx = (EngineContext_s *)((*audio_context)->_internal);
assert(ctx != NULL);
// destroy buffer queue audio player object, and invalidate all associated interfaces
if (ctx->bqPlayerObject != NULL) {
(*(ctx->bqPlayerObject))->Destroy(ctx->bqPlayerObject);
ctx->bqPlayerObject = NULL;
ctx->bqPlayerPlay = NULL;
ctx->bqPlayerBufferQueue = NULL;
}
// destroy output mix object, and invalidate all associated interfaces
if (ctx->outputMixObject != NULL) {
(*(ctx->outputMixObject))->Destroy(ctx->outputMixObject);
ctx->outputMixObject = NULL;
}
// destroy engine object, and invalidate all associated interfaces
if (ctx->engineObject != NULL) {
(*(ctx->engineObject))->Destroy(ctx->engineObject);
ctx->engineObject = NULL;
ctx->engineEngine = NULL;
}
if (ctx->mixBuf) {
FREE(ctx->mixBuf);
}
assert(ctx->voices == NULL && "incorrect API usage");
SLVoice *voice = ctx->recycledVoices;
while (voice) {
SLVoice *vkill = voice;
voice = voice->next;
_opensl_destroyVoice(vkill);
}
memset(ctx, 0x0, sizeof(EngineContext_s));
FREE(ctx);
memset(*audio_context, 0x0, sizeof(AudioContext_s));
FREE(*audio_context);
return 0;
}
static long opensles_systemSetup(INOUT AudioContext_s **audio_context) {
assert(*audio_context == NULL);
EngineContext_s *ctx = NULL;
SLresult result = -1;
opensles_audio_backend.systemSettings.sampleRateHz = android_deviceSampleRateHz;
opensles_audio_backend.systemSettings.bytesPerSample = 2;
if (android_deviceSampleRateHz <= 22050/*sentinel in DevicePropertyCalculator.java*/) {
android_stereoBufferSubmitSizeSamples >>= 1; // value from Android/Java DevicePropertyCalculator.java seems to be pre-multiplied by channel size?
}
opensles_audio_backend.systemSettings.monoBufferSizeSamples = android_deviceSampleRateHz * audio_getLatency();
opensles_audio_backend.systemSettings.stereoBufferSizeSamples = android_deviceSampleRateHz * audio_getLatency();
if (android_stereoBufferSubmitSizeSamples<<2 > opensles_audio_backend.systemSettings.stereoBufferSizeSamples) {
opensles_audio_backend.systemSettings.stereoBufferSizeSamples = android_stereoBufferSubmitSizeSamples<<2;
LOG("Changing stereo buffer size to be %lu samples", (unsigned long)opensles_audio_backend.systemSettings.stereoBufferSizeSamples);
}
if (android_monoBufferSubmitSizeSamples<<2 > opensles_audio_backend.systemSettings.monoBufferSizeSamples) {
opensles_audio_backend.systemSettings.monoBufferSizeSamples = android_monoBufferSubmitSizeSamples<<2;
LOG("Changing mono buffer size to be %lu samples", (unsigned long)opensles_audio_backend.systemSettings.monoBufferSizeSamples);
}
#warning TODO FIXME ^^^^^ need a dynamic bufferSize calculation/calibration routine to determine optimal buffer size for device ... may also need a user-initiated calibration too
do {
//
// Engine creation ...
//
ctx = CALLOC(1, sizeof(EngineContext_s));
if (!ctx) {
result = -1;
break;
}
ctx->submitSize = android_stereoBufferSubmitSizeSamples * opensles_audio_backend.systemSettings.bytesPerSample * NUM_CHANNELS;
ctx->mixBuf = CALLOC(1, ctx->submitSize);
if (ctx->mixBuf == NULL) {
ERRLOG("OOPS, Error allocating %lu bytes", (unsigned long)ctx->submitSize);
break;
}
// create basic engine
result = slCreateEngine(&(ctx->engineObject), 0, NULL, /*engineMixIIDCount:*/0, /*engineMixIIDs:*/NULL, /*engineMixReqs:*/NULL);
if (result != SL_RESULT_SUCCESS) {
ERRLOG("Could not create OpenSLES Engine : %lu", (unsigned long)result);
break;
}
// realize the engine
result = (*(ctx->engineObject))->Realize(ctx->engineObject, /*asynchronous_realization:*/SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS) {
ERRLOG("Could not realize the OpenSLES Engine : %lu", (unsigned long)result);
break;
}
// get the actual engine interface
result = (*(ctx->engineObject))->GetInterface(ctx->engineObject, SL_IID_ENGINE, &(ctx->engineEngine));
if (result != SL_RESULT_SUCCESS) {
ERRLOG("Could not get the OpenSLES Engine : %lu", (unsigned long)result);
break;
}
//
// Output Mix ...
//
result = (*(ctx->engineEngine))->CreateOutputMix(ctx->engineEngine, &(ctx->outputMixObject), 0, NULL, NULL);
if (result != SL_RESULT_SUCCESS) {
ERRLOG("Could not create output mix : %lu", (unsigned long)result);
break;
}
// realize the output mix
result = (*(ctx->outputMixObject))->Realize(ctx->outputMixObject, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS) {
ERRLOG("Could not realize the output mix : %lu", (unsigned long)result);
break;
}
// create soundcore API wrapper
if ((*audio_context = CALLOC(1, sizeof(AudioContext_s))) == NULL) {
result = -1;
ERRLOG("OOPS, Not enough memory");
break;
}
//
// OpenSLES buffer queue player setup
//
// configure audio source
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
.numBuffers = 2,
#warning FIXME TODO ... verify 2 numBuffers is best
};
SLDataFormat_PCM format_pcm = {
.formatType = SL_DATAFORMAT_PCM,
.numChannels = 2,
.samplesPerSec = opensles_audio_backend.systemSettings.sampleRateHz * 1000,
.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16,
.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16,
.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
.endianness = SL_BYTEORDER_LITTLEENDIAN,
};
SLDataSource audioSrc = {
.pLocator = &loc_bufq,
.pFormat = &format_pcm,
};
// configure audio sink
SLDataLocator_OutputMix loc_outmix = {
.locatorType = SL_DATALOCATOR_OUTPUTMIX,
.outputMix = ctx->outputMixObject,
};
SLDataSink audioSnk = {
.pLocator = &loc_outmix,
.pFormat = NULL,
};
// create audio player
#define _NUM_INTERFACES 3
const SLInterfaceID ids[_NUM_INTERFACES] = {
SL_IID_BUFFERQUEUE,
SL_IID_EFFECTSEND,
//SL_IID_MUTESOLO,
SL_IID_VOLUME,
};
const SLboolean req[_NUM_INTERFACES] = {
SL_BOOLEAN_TRUE,
SL_BOOLEAN_TRUE,
//numChannels == 1 ? SL_BOOLEAN_FALSE : SL_BOOLEAN_TRUE,
SL_BOOLEAN_TRUE,
};
result = (*(ctx->engineEngine))->CreateAudioPlayer(ctx->engineEngine, &(ctx->bqPlayerObject), &audioSrc, &audioSnk, _NUM_INTERFACES, ids, req);
if (result != SL_RESULT_SUCCESS) {
ERRLOG("OOPS, could not create the BufferQueue player object : %lu", (unsigned long)result);
break;
}
// realize the player
result = (*(ctx->bqPlayerObject))->Realize(ctx->bqPlayerObject, /*asynchronous_realization:*/SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS) {
ERRLOG("OOPS, could not realize the BufferQueue player object : %lu", (unsigned long)result);
break;
}
// get the play interface
result = (*(ctx->bqPlayerObject))->GetInterface(ctx->bqPlayerObject, SL_IID_PLAY, &(ctx->bqPlayerPlay));
if (result != SL_RESULT_SUCCESS) {
ERRLOG("OOPS, could not get the play interface : %lu", (unsigned long)result);
break;
}
// get the buffer queue interface
result = (*(ctx->bqPlayerObject))->GetInterface(ctx->bqPlayerObject, SL_IID_BUFFERQUEUE, &(ctx->bqPlayerBufferQueue));
if (result != SL_RESULT_SUCCESS) {
ERRLOG("OOPS, could not get the BufferQueue play interface : %lu", (unsigned long)result);
break;
}
// register callback on the buffer queue
result = (*(ctx->bqPlayerBufferQueue))->RegisterCallback(ctx->bqPlayerBufferQueue, bqPlayerCallback, ctx);
if (result != SL_RESULT_SUCCESS) {
ERRLOG("OOPS, could not register BufferQueue callback : %lu", (unsigned long)result);
break;
}
(*audio_context)->_internal = ctx;
(*audio_context)->CreateSoundBuffer = &opensl_createSoundBuffer;
(*audio_context)->DestroySoundBuffer = &opensl_destroySoundBuffer;
LOG("Successfully created OpenSLES engine and buffer queue");
} while (0);
if (result != SL_RESULT_SUCCESS) {
if (ctx) {
AudioContext_s *ctxPtr = CALLOC(1, sizeof(AudioContext_s));
ctxPtr->_internal = ctx;
opensles_systemShutdown(&ctxPtr);
}
assert(*audio_context == NULL);
LOG("OpenSLES sound output disabled");
}
return result;
}
// pause all audio
static long opensles_systemPause(AudioContext_s *audio_context) {
LOG("OpenSLES pausing play");
EngineContext_s *ctx = (EngineContext_s *)(audio_context->_internal);
SLresult result = (*(ctx->bqPlayerPlay))->SetPlayState(ctx->bqPlayerPlay, SL_PLAYSTATE_PAUSED);
return 0;
}
static long opensles_systemResume(AudioContext_s *audio_context) {
LOG("OpenSLES resuming play");
SLuint32 state = 0;
EngineContext_s *ctx = (EngineContext_s *)(audio_context->_internal);
SLresult result = (*(ctx->bqPlayerPlay))->GetPlayState(ctx->bqPlayerPlay, &state);
do {
if (result != SL_RESULT_SUCCESS) {
ERRLOG("OOPS, could not get source state when attempting to resume : %lu", (unsigned long)result);
break;
}
if (state != SL_PLAYSTATE_PLAYING) {
ERRLOG("WARNING: possible audio lifecycle mismatch ... continuing anyway");
}
if (state == SL_PLAYSTATE_PAUSED) {
// Balanced resume OK here
SLresult result = (*(ctx->bqPlayerPlay))->SetPlayState(ctx->bqPlayerPlay, SL_PLAYSTATE_PLAYING);
} else if (state == SL_PLAYSTATE_STOPPED) {
// Do not resume for stopped state, let this get forced from CPU/speaker thread otherwise we starve. (The
// stopped state happens if user dynamically changed buffer parameters in menu settings which triggered an
// OpenSLES destroy/re-initialization ... e.g. audio_setLatency() was invoked)
}
} while (0);
return result;
}
static void _init_opensl(void) {
LOG("Initializing OpenSLES sound system");
assert(audio_backend == NULL && "there can only be one!");
opensles_audio_backend.setup = &opensles_systemSetup;
opensles_audio_backend.shutdown = &opensles_systemShutdown;
opensles_audio_backend.pause = &opensles_systemPause;
opensles_audio_backend.resume = &opensles_systemResume;
audio_backend = &opensles_audio_backend;
}
static __attribute__((constructor)) void __init_opensl(void) {
emulator_registerStartupCallback(CTOR_PRIORITY_EARLY, &_init_opensl);
}

137
audio/soundcore.c Normal file
View File

@ -0,0 +1,137 @@
/*
* Apple // emulator for *ix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2013-2015 Aaron Culliney
*
*/
/*
* Apple //e core sound system support. Source inspired/derived from AppleWin.
*
* 2015/10/01 AUDIO LIFECYCLE WARNING : CPU thread owns all audio, otherwise bad things may happen in system sound layer
* (OpenAL/OpenSLES/ALSA/etc)
*/
#include "common.h"
#define MAX_SOUND_DEVICES 100
static AudioContext_s *audioContext = NULL;
bool audio_isAvailable = false;
float audio_latencySecs = 0.25f;
AudioBackend_s *audio_backend = NULL;
//-----------------------------------------------------------------------------
long audio_createSoundBuffer(INOUT AudioBuffer_s **audioBuffer) {
// CPU thread owns audio lifecycle (see note above)
assert(pthread_self() == cpu_thread_id);
if (!audio_isAvailable) {
*audioBuffer = NULL;
return -1;
}
if (*audioBuffer) {
audio_destroySoundBuffer(audioBuffer);
}
long err = 0;
do {
if (!audioContext) {
ERRLOG("Cannot create sound buffer, no context");
err = -1;
break;
}
err = audioContext->CreateSoundBuffer(audioContext, audioBuffer);
if (err) {
break;
}
} while (0);
return err;
}
void audio_destroySoundBuffer(INOUT AudioBuffer_s **audioBuffer) {
// CPU thread owns audio lifecycle (see note above)
assert(pthread_self() == cpu_thread_id);
if (audioContext) {
audioContext->DestroySoundBuffer(audioContext, audioBuffer);
}
}
bool audio_init(void) {
// CPU thread owns audio lifecycle (see note above)
assert(pthread_self() == cpu_thread_id);
if (audio_isAvailable) {
return true;
}
do {
if (!audio_backend) {
LOG("No backend audio available, cannot initialize soundcore");
break;
}
if (audioContext) {
audio_backend->shutdown(&audioContext);
}
long err = audio_backend->setup((AudioContext_s**)&audioContext);
if (err) {
LOG("Failed to create an audio context!");
break;
}
audio_isAvailable = true;
} while (0);
return audio_isAvailable;
}
void audio_shutdown(void) {
// CPU thread owns audio lifecycle (see note above)
assert(pthread_self() == cpu_thread_id);
if (!audio_isAvailable) {
return;
}
audio_backend->shutdown(&audioContext);
audio_isAvailable = false;
}
void audio_pause(void) {
// CPU thread owns audio lifecycle (see note above)
// Deadlock on Kindle Fire 1st Gen if audio_pause() and audio_resume() happen off CPU thread ...
#ifdef __APPLE__
# warning FIXME TODO : this assert is firing on iOS port ... but the assert is valid ... fix soon
#else
assert(pthread_self() == cpu_thread_id);
#endif
if (!audio_isAvailable) {
return;
}
audio_backend->pause(audioContext);
}
void audio_resume(void) {
// CPU thread owns audio lifecycle (see note above)
assert(pthread_self() == cpu_thread_id);
if (!audio_isAvailable) {
return;
}
audio_backend->resume(audioContext);
}
void audio_setLatency(float latencySecs) {
audio_latencySecs = latencySecs;
}
float audio_getLatency(void) {
return audio_latencySecs;
}

152
audio/soundcore.h Normal file
View File

@ -0,0 +1,152 @@
/*
* Apple // emulator for *ix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2013-2015 Aaron Culliney
*
*/
/*
* Apple //e core sound system support. Source inspired/derived from AppleWin.
*
*/
#ifndef _SOUNDCORE_H_
#define _SOUNDCORE_H_
#define AUDIO_STATUS_PLAYING 0x00000001
#define AUDIO_STATUS_NOTPLAYING 0x08000000
// AppleWin-sourced default error increment and max adjustment values ...
#define SOUNDCORE_ERROR_INC 20
#define SOUNDCORE_ERROR_MAX 200
// Note to future self:
//
// Although output for the speaker could be MONO (and was at one point in time) ... we optimize the OpenSLES backend on
// Android to have just one buffer queue callback, where we need to mix both mockingboard and speaker samples.
//
// For now, just make everything use stereo for simplicity (including OpenAL backend).
#define NUM_CHANNELS 2
typedef struct AudioBuffer_s {
bool bActive; // Mockingboard ... refactor?
bool bMute; // Mockingboard ... refactor?
long nVolume; // Mockingboard ... refactor?
PRIVATE void *_internal;
// Get current number of queued bytes
long (*GetCurrentPosition)(struct AudioBuffer_s *_this, OUTPARM unsigned long *bytes_queued);
// This method obtains a valid write pointer to the sound buffer's audio data
long (*Lock)(struct AudioBuffer_s *_this, unsigned long write_bytes, INOUT int16_t **audio_ptr, OUTPARM unsigned long *audio_bytes);
// This method releases a locked sound buffer.
long (*Unlock)(struct AudioBuffer_s *_this, unsigned long audio_bytes);
// Get status (playing or not)
long (*GetStatus)(struct AudioBuffer_s *_this, OUTPARM unsigned long *status);
// Mockingboard-specific buffer replay
//long (*UnlockStaticBuffer)(struct AudioBuffer_s *_this, unsigned long audio_bytes);
//long (*Replay)(struct AudioBuffer_s *_this);
} AudioBuffer_s;
/*
* Creates a sound buffer object.
*/
long audio_createSoundBuffer(INOUT AudioBuffer_s **audioBuffer);
/*
* Destroy and nullify sound buffer object.
*/
void audio_destroySoundBuffer(INOUT AudioBuffer_s **pVoice);
/*
* Prepare the audio subsystem, including the backend renderer.
*/
bool audio_init(void);
/*
* Shutdown the audio subsystem and backend renderer.
*/
void audio_shutdown(void);
/*
* Pause the audio subsystem.
*/
void audio_pause(void);
/*
* Resume the audio subsystem.
*/
void audio_resume(void);
/*
* Set audio buffer latency
*/
void audio_setLatency(float latencySecs);
/*
* Get audio buffer latency
*/
float audio_getLatency(void);
/*
* Is the audio subsystem available?
*/
extern READONLY bool audio_isAvailable;
typedef struct AudioSettings_s {
/*
* Native device sample rate
*/
READONLY unsigned long sampleRateHz;
/*
* Native device bytes-per-sample (currently assuming 16bit/2byte samples)
*/
READONLY unsigned long bytesPerSample;
/*
* Native mono min/ideal buffer size in samples
*/
READONLY unsigned long monoBufferSizeSamples;
/*
* Native stereo min/ideal buffer size in samples
*/
READONLY unsigned long stereoBufferSizeSamples;
} AudioSettings_s;
// ----------------------------------------------------------------------------
// Private audio backend APIs
typedef struct AudioContext_s {
PRIVATE void *_internal;
PRIVATE long (*CreateSoundBuffer)(const struct AudioContext_s *sound_system, INOUT AudioBuffer_s **buffer);
PRIVATE long (*DestroySoundBuffer)(const struct AudioContext_s *sound_system, INOUT AudioBuffer_s **buffer);
} AudioContext_s;
typedef struct AudioBackend_s {
AudioSettings_s systemSettings;
// basic backend functionality controlled by soundcore
PRIVATE long (*setup)(INOUT AudioContext_s **audio_context);
PRIVATE long (*shutdown)(INOUT AudioContext_s **audio_context);
PRIVATE long (*pause)(AudioContext_s *audio_context);
PRIVATE long (*resume)(AudioContext_s *audio_context);
} AudioBackend_s;
// Audio backend registered at CTOR time
extern AudioBackend_s *audio_backend;
#endif /* whole file */

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

4386
schematics/aiie.brd Normal file

File diff suppressed because it is too large Load Diff

10061
schematics/aiie.sch Normal file

File diff suppressed because it is too large Load Diff

516
schematics/eagle.epf Normal file
View File

@ -0,0 +1,516 @@
[Eagle]
Version="08 02 01"
Platform="Mac OS X"
Globals="Globals"
Desktop="Desktop"
[Globals]
AutoSaveProject=1
UsedLibrary="/Applications/EAGLE-8.2.1/lbr/esp8266modules.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/40xx.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/41xx.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/45xx.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/74ac-logic.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/74ttl-din.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/74xx-eu.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/74xx-little-de.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/74xx-little-us.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/74xx-us.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/751xx.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/IQD-Frequency-Products.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/advanced-test-technologies.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/agilent-technologies.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/allegro.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/altera-cyclone-II.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/altera-cyclone-III.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/altera-stratix-iv.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/altera.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/am29-memory.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/amd-mach.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/amd.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/amis.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/analog-devices.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/aplus.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/ase.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/atmel.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/austriamicrosystems.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/avago.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/axis.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/battery.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/belton-engineering.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/burr-brown.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/busbar.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/buzzer.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/c-trimm.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/california-micro-devices.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/capacitor-wima.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/chipcard-siemens.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/cirrus-logic.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-3m.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-4ucon.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-amp-champ.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-amp-micromatch.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-amp-mt.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-amp-mt6.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-amp-quick.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-amp-te.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-amp.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-amphenol.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-avx.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-berg.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-bosch.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-chipcard-iso7816.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-coax.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-commcon.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-conrad.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-cpci.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-cui.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-cypressindustries.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-deutsch.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-dil.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-ebyelectro.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-elco.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-erni.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-faston.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-fci.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-friwo.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-garry.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-harting-h.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-harting-ml.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-harting-v.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-harting.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-hirose.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-hirschmann.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-jack.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-jae.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-jst.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-kycon.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-kyocera-elco.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-lemo.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-leotronics.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-lsta.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-lstb.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-lumberg.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-ml.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-molex.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-neutrik_ag.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-omron.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-panasonic.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-panduit.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-pc.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-pc104.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-phoenix-254.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-phoenix-3.81.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-phoenix-350.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-phoenix-500.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-phoenix-508.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-phoenix-762.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-phoenix-me_max.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-phoenix-mkds_5.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-phoenix-smkdsp.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-ptr500.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-pulse.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-rib.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-samtec.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-shallin.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-shiua-chyuan.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-stewart.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-stocko.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-subd.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-sullinselectronics.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-thomas-betts.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-tyco.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-tycoelectronics.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-vg.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-wago-500.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-wago-508.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-wago.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-wago255.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-weidmueller-sl35.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-wenzhou-yihua.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-xmultiple.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/con-yamaichi.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/crystal.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/csr.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/cypress.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/davicom.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/dc-dc-converter.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/dimensions.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/diode.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/discrete.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/display-hp.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/display-kingbright.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/display-lcd.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/docu-dummy.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/eagle-ltspice.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/ecl.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/em-microelectronic.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/etx-board.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/exar.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/fairchild-semic.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/farnell.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/fiber-optic-hp.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/fiber-optic-siemens.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/fifo.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/flexipanel.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/fox-electronics.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/frames.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/freescale.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/ftdichip.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/fujitsu.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/fuse.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/gennum.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/halo-electronics.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/heatsink.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/holes.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/holtek.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/ic-package.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/inductor-coilcraft.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/inductor-neosid.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/inductor-nkl.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/inductors.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/infineon-tricore.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/infineon.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/intersil-techwell.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/intersil.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/ir.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/isd.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/johanson-technology.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/jorjlib.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/jump-0r-smd.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/jumper.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/lantronix.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/lattice.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/lc-filter.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/led-7-segment.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/led-citizen-electronics.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/led-lumiled.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/led.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/lem.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/linear-technology.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/linear.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/linx.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/logo.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/lprs.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/lsi-computer-systems.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/lumiled.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/marks.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/maxim.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/maxstream.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/melexis.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/memory-hitachi.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/memory-idt.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/memory-micron.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/memory-motorola-dram.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/memory-nec.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/memory-samsung.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/memory-sram.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/memory.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/mems.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micrel.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-cyrod.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-fujitsu.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-harris.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-hitachi.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-infineon.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-intel.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-mc68000.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-motorola.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-philips.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-renesas.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-samsung.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micro-siemens.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/microchip.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micron.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/micronas.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/microphon.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/microwave.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/midori-sensor.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/minicircuits.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/mitsubishi-semiconductor.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/motorola-sensor-driver.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/murata-filter.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/murata-sensor.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/nanotec.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/national-instruments.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/national-semiconductor.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/nec-lqfp100-pack.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/nec.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/nrj-semiconductor.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/omnivision.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/on-semiconductor.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/opto-honeywell-3000.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/opto-honeywell-4000.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/opto-honeywell.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/opto-micro-linear.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/opto-trans-siemens.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/opto-transmittter-hp.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/opto-vishay.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/optocoupler.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/pal.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/philips-semiconductors.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/photo-elements.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/piher.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/pinhead.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/plcc-socket.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/pld-intel.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/plxtech.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/pot-vitrohm.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/pot-xicor.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/pot.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/ptc-ntc.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/quantum-research-group.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/rcl.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/recom-international.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/rectifier.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/ref-packages-longpad.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/ref-packages.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/relay.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/renesas.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/resistor-bourns.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/resistor-dil.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/resistor-net.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/resistor-power.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/resistor-ruf.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/resistor-shunt.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/resistor-sil.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/resistor.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/rf-micro-devices.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/rf-solutions.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/rohm.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/roundsolutions.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/semicon-smd-ipc.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/sensor-comus-group.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/sensor-heraeus.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/sensor-infratec.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/sharp.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/silabs.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/sim-technology.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/sipex.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/smd-ipc.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/smd-special.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/solomon-systech.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/solpad.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/speaker.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/special-drill.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/special.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/st-microelectronics.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/stm32xx.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/supertex.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/supply1.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/supply2.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/switch-alps.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/switch-coto.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/switch-dil.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/switch-misc.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/switch-omron.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/switch-raychem.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/switch-reed.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/switch.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/telcom.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/telecontrolli.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/telefunken.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/testpad.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/texas-sn55-sn75.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/texas.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/toshiba.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/traco-electronic.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/trafo-bei.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/trafo-hammondmfg.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/trafo-siemens.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/trafo-xicon.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/trafo.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/transformer-pulse.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/transistor-fet.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/transistor-neu-to92.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/transistor-npn.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/transistor-pnp.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/transistor-power.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/transistor-small-signal.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/transistor.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/triac.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/trimble.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/tripas.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/u-blox.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/uln-udn.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/v-reg-micrel.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/v-reg.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/varistor.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/wafer-scale-psd.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/wirepad.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/xicor.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/xilinx-virtex-v5.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/xilinx-xc18v.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/xilinx-xc9.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/xilinx-xcv.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/zetex.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/zilog.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Switches.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_adc.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_arm.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_automotive.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_avr.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_battery.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_buzzer.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_communication.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_con-jst-xh.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_crystal.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_diode.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_eeZeeProp.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_electromech.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_enclosures.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_hero.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_inductor.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_linear.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_logic.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_LPCXpresso.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_memory.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_motordriver.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_opto.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_pic.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_rcl.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_regulator.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_speech.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_transformer.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_transistor.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt_wiicam.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library/bt.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-lbr/button-dpdt-7mm.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/eagle-library.git/jorjlib.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/LilyPad-Wearables.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Aesthetics.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Batteries.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Boards.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Capacitors.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Clocks.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Coils.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Connectors.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-DiscreteSemi.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Displays.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Electromechanical.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Fuses.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-GPS.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Hardware.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-IC-Amplifiers.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-IC-Comms.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-IC-Conversion.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-IC-Logic.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-IC-Memory.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-IC-Microcontroller.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-IC-Power.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-IC-Special-Function.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Jumpers.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-LED.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-PowerSymbols.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Resistors.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Retired.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-RF.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/SparkFun-Sensors.lbr"
UsedLibrary="/Applications/EAGLE-7.3.0/lbr/SparkFun-Eagle-Libraries/User-Submitted.lbr"
UsedLibrary="/Users/jorj/Desktop/jorj-git/Tall-Dog-Public/Teensy-Breakout-Eagle-Library.lbr"
[Win_1]
Type="Board Editor"
Loc="319 23 1919 1173"
State=0
Number=2
File="aiie.brd"
View="-90.647 -7.32884 124.987 166.653"
WireWidths=" 0.0762 0.1016 0.127 0.15 0.2 0.2032 0.254 0.3048 0.4064 0.508 0.6096 0.8128 1.016 1.27 2.54 0.1524"
PadDiameters=" 0.254 0.3048 0.4064 0.6096 0.8128 1.016 1.27 1.4224 1.6764 1.778 1.9304 2.1844 2.54 3.81 6.4516 0"
PadDrills=" 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.55 0.65 0.7 0.75 0.8 0.85 0.9 1 0.6"
ViaDiameters=" 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9 0.95 1 1.05 1.1 1.15 1.2 1.3 0"
ViaDrills=" 0.2 0.25 0.3 0.4 0.45 0.5 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9 1 0.35"
HoleDrills=" 0.2 0.25 0.3 0.4 0.45 0.5 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9 1 0.35"
TextSizes=" 0.3048 0.4064 0.6096 0.8128 1.016 1.4224 1.6764 1.9304 2.1844 3.81 6.4516 1.778 0.0508 1.27 5.08 2.54"
PolygonSpacings=" 0.254 0.3048 0.4064 0.6096 0.8128 1.016 1.4224 1.6764 1.778 1.9304 2.1844 2.54 3.81 5.08 6.4516 1.27"
PolygonIsolates=" 0.254 0.3048 0.4064 0.6096 0.8128 1.016 1.27 1.4224 1.6764 1.778 1.9304 2.1844 2.54 3.81 6.4516 0"
MiterRadiuss=" 0.254 0.3175 0.635 1.27 2.54 1 2 2.5 5 7.5 10 0"
DimensionWidths=" 0 0.127 0.254 0.1 0.26 0.13"
DimensionExtWidths=" 0.127 0.254 0.1 0.13 0.26 0"
DimensionExtLengths=" 1.27 2.54 1 2 3 0"
DimensionExtOffsets=" 1.27 2.54 1 2 3 0"
SmdSizes=" 0.3048 0.1524 0.4064 0.2032 0.6096 0.3048 0.8128 0.4064 1.016 0.508 1.27 0.6604 1.4224 0.7112 1.6764 0.8128 1.778 0.9144 1.9304 0.9652 2.1844 1.0668 2.54 1.27 3.81 1.9304 5.08 2.54 6.4516 3.2512 1.27 0.635"
WireBend=1
WireBendSet=0
WireCap=1
MiterStyle=0
PadShape=0
ViaShape=1
PolygonPour=0
PolygonRank=1
PolygonThermals=1
PolygonOrphans=0
TextRatio=8
DimensionUnit=1
DimensionPrecision=2
DimensionShowUnit=0
PinDirection=3
PinFunction=0
PinLength=2
PinVisible=3
SwapLevel=0
ArcDirection=0
AddLevel=2
PadsSameType=0
Layer=22
[Win_2]
Type="Control Panel"
Loc="0 23 1199 622"
State=2
Number=0
[Win_3]
Type="Schematic Editor"
Loc="138 88 1525 1031"
State=2
Number=1
File="aiie.sch"
View="64.3418 -21.2163 222.481 122.65"
WireWidths=" 0.0762 0.1016 0.127 0.15 0.2 0.2032 0.254 0.3048 0.4064 0.508 0.6096 0.8128 1.016 1.27 2.54 0.1524"
PadDiameters=" 0.254 0.3048 0.4064 0.6096 0.8128 1.016 1.27 1.4224 1.6764 1.778 1.9304 2.1844 2.54 3.81 6.4516 0"
PadDrills=" 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.55 0.65 0.7 0.75 0.8 0.85 0.9 1 0.6"
ViaDiameters=" 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9 0.95 1 1.05 1.1 1.15 1.2 1.3 0"
ViaDrills=" 0.2 0.25 0.3 0.4 0.45 0.5 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9 1 0.35"
HoleDrills=" 0.2 0.25 0.3 0.4 0.45 0.5 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9 1 0.35"
TextSizes=" 0.254 0.3048 0.4064 0.6096 0.8128 1.016 1.27 1.4224 1.6764 1.9304 2.1844 2.54 3.81 5.08 6.4516 1.778"
PolygonSpacings=" 0.254 0.3048 0.4064 0.6096 0.8128 1.016 1.4224 1.6764 1.778 1.9304 2.1844 2.54 3.81 5.08 6.4516 1.27"
PolygonIsolates=" 0.254 0.3048 0.4064 0.6096 0.8128 1.016 1.27 1.4224 1.6764 1.778 1.9304 2.1844 2.54 3.81 6.4516 0"
MiterRadiuss=" 0.254 0.3175 0.635 1.27 2.54 1 2 2.5 5 7.5 10 0"
DimensionWidths=" 0 0.127 0.254 0.1 0.26 0.13"
DimensionExtWidths=" 0.127 0.254 0.1 0.13 0.26 0"
DimensionExtLengths=" 1.27 2.54 1 2 3 0"
DimensionExtOffsets=" 1.27 2.54 1 2 3 0"
SmdSizes=" 0.3048 0.1524 0.4064 0.2032 0.6096 0.3048 0.8128 0.4064 1.016 0.508 1.27 0.6604 1.4224 0.7112 1.6764 0.8128 1.778 0.9144 1.9304 0.9652 2.1844 1.0668 2.54 1.27 3.81 1.9304 5.08 2.54 6.4516 3.2512 1.27 0.635"
WireBend=0
WireBendSet=31
WireCap=1
MiterStyle=0
PadShape=0
ViaShape=1
PolygonPour=0
PolygonRank=0
PolygonThermals=1
PolygonOrphans=0
TextRatio=8
DimensionUnit=1
DimensionPrecision=2
DimensionShowUnit=0
PinDirection=3
PinFunction=0
PinLength=2
PinVisible=3
SwapLevel=0
ArcDirection=0
AddLevel=2
PadsSameType=0
Layer=91
Views=" 1: 64.3418 -21.2163 222.481 122.65"
Sheet="1"
[Desktop]
Screen="640 480"
Window="Win_1"
Window="Win_2"
Window="Win_3"