Skip to content

Instantly share code, notes, and snippets.

@Airrr17 Airrr17/ay_player.ino forked from anteo/ay_player.ino
Last active Jun 14, 2018

Embed
What would you like to do?
Arduino UNO + AY player + OLED
/////////////////////////////////////////////////////////////
//0.96" 128x32 i2c OLED. SDA=A4, SCL=A5
/////////////////////////////////////////////////////////////
#include <SPI.h>
#include "SdFat.h"
#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"
#define I2C_ADDRESS 0x3C
//#define RST_PIN -1
SSD1306AsciiAvrI2c oled;
const byte pinClock = 3; // AY38910 clock
const byte pinReset = 2; // AY38910 reset
const byte pinBC1 = 8; // AY38910 BC1
const byte pinBDIR = 9; // AY38910 BDIR
const byte pinSHCP = 4; // 74HC595 clock
const byte pinSTCP = 5; // 74HC595 latch
const byte pinDS = 6; // 74HC595 data
const byte pinSkip = 7; // skip to next random song
const byte pinCS = 10; // SD card select (CS)
const char *supportedFileExt = ".psg";
const byte fileExtLen = strlen(supportedFileExt);
const int bufSize = 300;
const bool demoMode = false;
const int demoLen = 10000;
const int demoFadeLen = 500;
enum AYMode {INACTIVE, WRITE, LATCH};
SdFat sd;
SdFile fp;
SdFile dir;
byte volumeA;
byte volumeB;
byte volumeC;
byte playBuf[bufSize];
bool playbackFinished = true;
bool playbackSkip;
int filesCount;
int fileNum;
int loadPos;
int playPos;
int skipCnt;
int totalPos;
float globalVolume;
void setup() {
Serial.begin(9600);
setupPins();
generateSeed();
setupTimer();
init2MhzClock();
resetAY();
oled.begin(&Adafruit128x32, I2C_ADDRESS);
oled.setFont(Adafruit5x7);
oled.clear();
initSD();
loadRandomFile();
}
void setupPins() {
pinMode(pinBC1, OUTPUT);
pinMode(pinBDIR, OUTPUT);
pinMode(pinReset, OUTPUT);
pinMode(pinClock, OUTPUT);
pinMode(pinSHCP, OUTPUT);
pinMode(pinSTCP, OUTPUT);
pinMode(pinDS, OUTPUT);
pinMode(pinSkip, INPUT);
}
void generateSeed() {
unsigned long seed = seedOut(31);
Serial.print("Seed = ");
Serial.println(seed);
randomSeed(seed);
}
unsigned int bitOut(void) {
static unsigned long firstTime = 1, prev = 0;
unsigned long bit1 = 0, bit0 = 0, x = 0, port = 6, limit = 99;
if (firstTime)
{
firstTime = 0;
prev = analogRead(port);
}
while (limit--)
{
x = analogRead(port);
bit1 = (prev != x ? 1 : 0);
prev = x;
x = analogRead(port);
bit0 = (prev != x ? 1 : 0);
prev = x;
if (bit1 != bit0)
break;
}
return bit1;
}
unsigned long seedOut(unsigned int noOfBits) {
// return value with 'noOfBits' random bits set
unsigned long seed = 0;
for (int i = 0; i < noOfBits; ++i)
seed = (seed << 1) | bitOut();
return seed;
}
void loop() {
loadNextByte();
if (playbackFinished) {
resetAY();
loadRandomFile();
}
}
void setupTimer() {
cli();
TCCR1A = 0;
TCCR1B = _BV(WGM12) | _BV(CS12);
TIMSK1 = _BV(OCIE1A);
TCNT1 = 0;
OCR1A = 1250;
sei();
}
void initSD() {
// Serial.print("ISD.");
if (!sd.begin(pinCS, SD_SCK_MHZ(50))) {
Serial.println("-");
sd.initErrorHalt();
}
// Serial.println("+");
loadDirectory();
// Serial.println("done!");
}
void loadRandomFile() {
SdFile file;
Serial.println("Srf.");
fileNum = random(0, filesCount); //-1
int n = fileNum;
dir.close();
if (!dir.open("/", O_READ)) {
sd.errorHalt("Orf");
}
while (file.openNext(&dir, O_READ)) {
if (checkFile(file)) {
if (n <= 0) {
playFile(file);
return;
}
n--;
}
file.close();
}
sd.errorHalt("Nmf.");
}
void playFile(SdFile entry) {
fp.close();
fp = entry;
// Serial.print("P");
// fp.printName(&Serial);
// Serial.println("...");
loadPos = playPos = totalPos = 0;
///////////////////////////Airrr///////////////////////////////////////////
oled.clear();
oled.print("File: ");
char buf[4];
sprintf (buf, "%03d", fileNum + 1);
oled.print(buf);
oled.print(" of ");
sprintf (buf, "%03d", filesCount);
oled.println(buf);
oled.print("Name: ");
char name[15];
fp.getName(name, 15);
oled.println(name);
oled.print("Size: ");
oled.print(fp.fileSize());
oled.println(" bytes");
////////////////////////////////////Airrr///////////////////////////////////////////
while (fp.available()) {
byte b = fp.read();
if (b == 0xFF) break;
}
playbackFinished = false;
}
bool checkFile(SdFile entry) {
char name[256];
entry.getName(name, 255);
return !entry.isHidden() && !entry.isDir() && (strlen(name) > fileExtLen &&
!strcasecmp(name + strlen(name) - fileExtLen, supportedFileExt));
}
void loadDirectory() {
SdFile file;
if (!dir.open("/", O_READ)) {
sd.errorHalt("Open root failed");
}
while (file.openNext(&dir, O_READ)) {
if (checkFile(file)) {
filesCount++;
}
file.close();
}
}
void resetAY() {
setAYMode(INACTIVE);
volumeA = volumeB = volumeC = 0;
globalVolume = 1;
digitalWrite(pinReset, LOW);
delay(50);
digitalWrite(pinReset, HIGH);
delay(50);
}
void init2MhzClock() {
const int PERIOD = 9; // 9 CPU cycles ~ 1.778 MHz
TCCR2B = 0; // stop timer
TCNT2 = 0; // reset timer
TCCR2A = _BV(COM2B1) // non-inverting PWM on OC2B
| _BV(WGM20) // fast PWM mode, TOP = OCR2A
| _BV(WGM21); // ...ditto
TCCR2B = _BV(WGM22) | _BV(CS20); // ...ditto
OCR2A = PERIOD - 1;
OCR2B = PERIOD / 2 - 1;
}
void setAYMode(AYMode mode) {
switch (mode) {
case INACTIVE:
PORTB &= B11111100;
break;
case WRITE:
PORTB |= B00000010;
break;
case LATCH:
PORTB |= B00000011;
break;
}
}
void setVolume(float volume) {
globalVolume = volume;
writeAY(8, volumeA);
writeAY(9, volumeB);
writeAY(10, volumeC);
}
void writeAY(byte port, byte data) {
if (port == 8 || port == 9 || port == 10) {
if (port == 8) volumeA = data;
if (port == 9) volumeB = data;
if (port == 10) volumeC = data;
data = (byte)(data * globalVolume);
}
setAYMode(INACTIVE);
digitalWrite(pinSTCP, LOW);
shiftOut(pinDS, pinSHCP, MSBFIRST, port);
digitalWrite(pinSTCP, HIGH);
setAYMode(LATCH);
setAYMode(INACTIVE);
digitalWrite(pinSTCP, LOW);
shiftOut(pinDS, pinSHCP, MSBFIRST, data);
digitalWrite(pinSTCP, HIGH);
setAYMode(WRITE);
setAYMode(INACTIVE);
}
bool loadNextByte() {
if (loadPos == playPos - 1 || loadPos == bufSize - 1 && playPos == 0)
return false;
byte b = fp.available() ? fp.read() : 0xFD;
playBuf[loadPos++] = b;
if (loadPos == bufSize) loadPos = 0;
return true;
}
bool isNextByteAvailable() {
return playPos != loadPos;
}
byte getNextByte() {
if (!isNextByteAvailable()) return 0;
byte b = playBuf[playPos++];
if (playPos == bufSize) playPos = 0;
totalPos++;
return b;
}
void displayOLED() {
if (playbackFinished) return;
//////////////////////////////////////////Airrr/////////////////////////////////////////////
oled.setCursor(0, 24);
oled.print(" ");
oled.setCursor(volumeC / 1.5, 24);
oled.print(">");
oled.setCursor((122 - volumeA / 1.5), 24);
oled.print("<");
oled.setCursor((62 + volumeB / 1.5), 24);
oled.print("]");
oled.setCursor((57 - volumeB / 1.5), 24);
oled.print("[");
}
///////////////////////////////////////////Airrr////////////////////////////////////////////
void checkDemo() {
if (demoMode && totalPos >= demoLen && !playbackFinished) {
int demoPos = totalPos - demoLen;
setVolume(1.0 - demoPos / (float)demoFadeLen);
if (demoPos >= demoFadeLen) playbackFinished = true;
}
}
void playNotes() {
if (digitalRead(pinSkip) == LOW)
playbackSkip = true;
else if (playbackSkip) {
playbackSkip = false;
playbackFinished = true;
}
if (playbackFinished || --skipCnt > 0)
return;
int oldPlayPos = playPos;
int oldTotalPos = totalPos;
while (isNextByteAvailable()) {
byte b = getNextByte();
if (b == 0xFF) {
break;
} else if (b == 0xFD) {
playbackFinished = true;
break;
} else if (b == 0xFE) {
if (isNextByteAvailable()) {
skipCnt = getNextByte();
skipCnt *= 4;
break;
}
} else if (b <= 0xFC) {
if (isNextByteAvailable()) {
byte v = getNextByte();
if (b < 16) writeAY(b, v);
}
}
}
if (!isNextByteAvailable()) {
playPos = oldPlayPos;
totalPos = oldTotalPos;
}
}
ISR(TIMER1_COMPA_vect) {
playNotes();
displayOLED();
checkDemo();
}
@Airrr17

This comment has been minimized.

Copy link
Owner Author

Airrr17 commented Jun 14, 2018

Edited to use with 128x32 oled instead of 16x2 LCD.
Showing selected file# of total files and filesize.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.