Автономный проигрыватель мелодий с компьютера ZX Spectrum на Arduino с минимальным количеством деталей.




Похоже на то, что спектрумовские мелодии навсегда останутся в моём сердце, так как я регулярно слушаю любимые композиции, используя замечательный бульбовский проигрыватель.



Но не очень удобно быть привязанным к компьютеру. Эту проблему я временно решал, используя не менее замечательный EEE PC. Но хотелось ещё большей миниатюрности.



Поиски в интернете привели на следующих красавцев:




Они восхитительны своей элементной базой, которая вызывает ностальгические воспоминания, но я понимал, что моя лень не позволит мне довести такой проект до конца.

Мне нужно было что-то небольшое. И вот — практически идеальный кандидат:

AVR AY-player
— играет файлы *.PSG
— поддерживаемая файловая система FAT16
— количество каталогов в корне диска 32
— количество файлов в каталоге 42 (итого 32*42=1344 файлов)
— сортировка каталогов и файлов в каталогах по первым двум буквам имени



Схема выглядит весьма приемлемой по размеру:


Конечно же нашёлся фатальный недостаток, который портил идиллию: нет режима случайного выбора композиции. (возможно стоило просто попросить автора добавить эту функцию в прошивку?).

Джва года я искал подходящий вариант, моё терпение кончилось и я решил действовать.

Исходя из моей фантастической лени, я выбрал минимальные телодвижения:
1. Берём Arduino Mini Pro, чтобы не возится с обвязкой.
2. Нужна SD-карта, чтобы где-то хранить музыку. Значит берём SD-shield.
3. Нужен музыкальный сопроцессор. Самый маленький — AY-3-8912.

Был ещё вариант сэмулировать сопроцессор программным путём, но хотелось «тёплого лампового звука», евпочя.

Для воспроизведения будем использовать PSG-формат.

Структура PSG-формата
Offset Number of byte Description
+0 3 Identifier 'PSG'
+3 1 Marker “End of Text” (1Ah)
+4 1 Version number
+5 1 Player frequency (for versions 10+)
+6 10 Data

Data — последовательности пар байтов записи в регистр.
Первый байт — номер регистра (от 0 до 0x0F), второй — значение.
Вместо номера регистра могут быть специальные маркеры: 0xFF, 0xFE или 0xFD
0xFD — конец композиции.
0xFF — ожидание 20 мс.
0xFE — следующий байт показывает сколько раз выждать по 80 мс.

Как конвертировать в PSG
1. Устанавливаем бульбовский проигрыватель.
2. Открываем плейлист кнопкой [PL].
3. Добавляем мелодии в плейлист.
4. Выбираем мелодию в списке, правой кнопкой вызываем меню, в нём Convert to PSG...
5. Сохраняем желательно под именем не длиннее 8 символов, иначе оно будет отображено не полнос~1.тью.

Начнём с подключения SD-карты. Лень подсказала взять стандартное подключение SD-shield и использовать стандартную библиотеку для работы с картой.

Единственное отличие — для удобства использовал 10 вывод в качестве сигнала выбора карты:


Для проверки берём стандартный скетч:

скетч списка файлов на карте
#include <SPI.h>
#include <SD.h>

void setup() {
  Serial.begin(9600);
  Serial.print("Initializing SD card...");

  if (!SD.begin(10)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  File root = SD.open("/");
  printDirectory(root);

  Serial.println("done!");
}

void loop() {
}

void printDirectory(File dir) {
  while (true) {
    File entry =  dir.openNextFile();
    if (!entry)  break;
    Serial.print(entry.name());
    if (!entry.isDirectory()) {
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
  }
}

Форматируем карту, пишем туда несколько файлов, запускам… не работает!
Вот у меня так всегда — наистандартнейшая задача — и сразу косяки.

Берём другую флешку — (была старенькая на 32Mb, берём новенькую на 2Gb) — ага, заработало, но через раз. Полчаса чесания лба, перестановка соединений поближе к карте (чтобы проводники были короче), развязочный конденсатор по питанию — и работать стало в 100% случаев. Ладно, едем дальше…

Теперь надо завести сопроцессор — ему нужна тактовая частота 1.75 МГц. Вместо того, чтобы спаять генератор на 14 МГц кварце и поставить делитель, тратим полдня на чтение доков по микроконтроллеру и узнаём, что можно сделать хардовые 1.77(7) МГц, используя быстрый ШИМ:

  pinMode(3, OUTPUT);
  TCCR2A = 0x23;
  TCCR2B = 0x09;
  OCR2A = 8;
  OCR2B = 3; 


Далее, заводим сброс музсопроцессора на пин 2, нижний ниббл шины данных на A0-A3, верхний на 4,5,6,7, BC1 на пин 8, BDIR на пин 9. Аудио выходы для простоты подключим в моно режиме:



На макетке:


Заливаем пробный скетч (откуда утащил массив не помню)
void resetAY(){
  pinMode(A0, OUTPUT); // D0
  pinMode(A1, OUTPUT);
  pinMode(A2, OUTPUT);
  pinMode(A3, OUTPUT); // D3
  
  pinMode(4, OUTPUT); // D4
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT); // D7
  
  pinMode(8, OUTPUT);  // BC1
  pinMode(9, OUTPUT);  // BDIR
  
  digitalWrite(8,LOW);
  digitalWrite(9,LOW);
  
  pinMode(2, OUTPUT);
  digitalWrite(2, LOW);
  delay(100);
  digitalWrite(2, HIGH);
  delay(100);
  
  for (int i=0;i<16;i++) ay_out(i,0);
}

void setupAYclock(){
  pinMode(3, OUTPUT);
  TCCR2A = 0x23;
  TCCR2B = 0x09;
  OCR2A = 8;
  OCR2B = 3; 
}

void setup() {
  setupAYclock();
  resetAY();
}

void ay_out(unsigned char port, unsigned char data){
  PORTB = PORTB & B11111100;

  PORTC = port & B00001111;
  PORTD = PORTD & B00001111;

  PORTB = PORTB | B00000011;
  delayMicroseconds(1);
  PORTB = PORTB & B11111100;

  PORTC = data & B00001111;
  PORTD = (PORTD & B00001111) | (data & B11110000);

  PORTB = PORTB | B00000010;
  delayMicroseconds(1);
  PORTB = PORTB & B11111100;
}

unsigned int cb = 0;

byte rawData[] = {
    0xFF, 0x00, 0x8E, 0x02, 0x38, 0x03, 0x02, 0x04, 0x0E, 0x05, 0x02, 0x07, 
    0x1A, 0x08, 0x0F, 0x09, 0x10, 0x0A, 0x0E, 0x0B, 0x47, 0x0D, 0x0E, 0xFF, 
    0x00, 0x77, 0x04, 0x8E, 0x05, 0x03, 0x07, 0x3A, 0x08, 0x0E, 0x0A, 0x0D, 
    0xFF, 0x00, 0x5E, 0x04, 0x0E, 0x05, 0x05, 0x0A, 0x0C, 0xFF, 0x04, 0x8E, 
    0x05, 0x06, 0x07, 0x32, 0x08, 0x00, 0x0A, 0x0A, 0xFF, 0x05, 0x08, 0x0A, 
    0x07, 0xFF, 0x04, 0x0E, 0x05, 0x0A, 0x0A, 0x04, 0xFF, 0x00, 0x8E, 0x04, 
    0x8E, 0x05, 0x00, 0x07, 0x1E, 0x08, 0x0F, 0x0A, 0x0B, 0x0D, 0x0E, 0xFF, 
    0x00, 0x77, 0x08, 0x0E, 0x0A, 0x06, 0xFF, 0x00, 0x5E, 0x07, 0x3E, 0x0A, 
    0x00, 0xFF, 0x07, 0x36, 0x08, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x8E, 0x07, 
    0x33, 0x08, 0x0B, 0x0A, 0x0F, 0x0D, 0x0E, 0xFF, 0x04, 0x77, 0x08, 0x06, 
    0x0A, 0x0E, 0xFF, 0x04, 0x5E, 0x07, 0x3B, 0x08, 0x00, 0xFF, 0x07, 0x1B, 
    0x0A, 0x00, 0xFF, 0xFF, 0xFF, 0x02, 0x1C, 0x03, 0x01, 0x04, 0x8E, 0x07, 
    0x33, 0x08, 0x0B, 0x0A, 0x0B, 0x0B, 0x23, 0x0D, 0x0E, 0xFF, 0x04, 0x77, 
    0x08, 0x06, 0x0A, 0x0A, 0xFF, 0x04, 0x5E, 0x07, 0x3B, 0x08, 0x00, 0x0A, 
    0x09, 0xFF, 0x07, 0x1B, 0x0A, 0x00, 0xFF, 0xFF, 0xFF, 0x02, 0x8E, 0x03, 
    0x00, 0x04, 0x0E, 0x05, 0x01, 0x07, 0x18, 0x08, 0x0F, 0x09, 0x0B, 0x0A, 
    0x0E, 0xFF, 0x00, 0x77, 0x02, 0x77, 0x04, 0x8E, 0x06, 0x01, 0x08, 0x0E, 
    0x09, 0x0A, 0x0A, 0x0D, 0xFF, 0x00, 0x5E, 0x02, 0x5E, 0x04, 0x0E, 0x05, 
    0x02, 0x06, 0x02, 0x09, 0x09, 0x0A, 0x0C, 0xFF, 0x02, 0x8E, 0x04, 0x8E, 
    0x07, 0x30, 0x08, 0x00, 0x09, 0x08, 0x0A, 0x0A, 0xFF, 0x02, 0x77, 0xFF,
    0xFF
}

void pseudoInterrupt(){
  while (rawData[cb]<0xFF) {
   ay_out(rawData[cb],rawData[cb+1]);
   cb++;
   cb++;
 }
 if (rawData[cb]==0xff) cb++;
 
 if (cb>20*12)  cb=0;
}

void loop() {
  delay(20);
  pseudoInterrupt();
}

И слышим полсекунды какой-то прекрасной мелодии! (на самом деле я тут ещё два часа ищу как я забыл отпустить ресет после инициализации).

На этом железная часть закончена, а в программной добавляем прерывания 50 Гц, считывание файла и запись в регистры сопроцессора.

Окончательный вариант программы
#include <SPI.h>
#include <SD.h>

void resetAY(){
  pinMode(A0, OUTPUT); // D0
  pinMode(A1, OUTPUT);
  pinMode(A2, OUTPUT);
  pinMode(A3, OUTPUT); // D3
  
  pinMode(4, OUTPUT); // D4
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT); // D7
  
  pinMode(8, OUTPUT);  // BC1
  pinMode(9, OUTPUT);  // BDIR
  
  digitalWrite(8,LOW);
  digitalWrite(9,LOW);
  
  pinMode(2, OUTPUT);
  digitalWrite(2, LOW);
  delay(100);
  digitalWrite(2, HIGH);
  delay(100);
  
  for (int i=0;i<16;i++) ay_out(i,0);
}


void setupAYclock(){
  pinMode(3, OUTPUT);
  TCCR2A = 0x23;
  TCCR2B = 0x09;
  OCR2A = 8;
  OCR2B = 3; 
}

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(4)+analogRead(5));

  initFile();

  setupAYclock();
  resetAY();
  setupTimer();
}

void setupTimer(){
  cli();
  
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;
  OCR1A = 1250;

  TCCR1B |= (1 << WGM12);
  TCCR1B |= (1 << CS12);
  TIMSK1 |= (1 << OCIE1A);

  sei();
}

void ay_out(unsigned char port, unsigned char data){
  PORTB = PORTB & B11111100;

  PORTC = port & B00001111;
  PORTD = PORTD & B00001111;

  PORTB = PORTB | B00000011;
  delayMicroseconds(1);
  PORTB = PORTB & B11111100;

  PORTC = data & B00001111;
  PORTD = (PORTD & B00001111) | (data & B11110000);

  PORTB = PORTB | B00000010;
  delayMicroseconds(1);
  PORTB = PORTB & B11111100;
}

unsigned int playPos = 0;
unsigned int fillPos = 0;
const int bufSize = 200;
byte playBuf[bufSize]; // 31 bytes per frame max, 50*31 = 1550 per sec, 155 per 0.1 sec

File fp;
boolean playFinished = false;

void loop() {
  fillBuffer();
  if (playFinished){
    fp.close();
    openRandomFile();
    playFinished = false;
  }
}

void fillBuffer(){
  int fillSz = 0;
  int freeSz = bufSize;
  if (fillPos>playPos) {
    fillSz = fillPos-playPos;
    freeSz = bufSize - fillSz;
  }
  if (playPos>fillPos) {
    freeSz = playPos - fillPos;
    fillSz = bufSize - freeSz;
  }
  
  freeSz--; // do not reach playPos
  while (freeSz>0){
    byte b = 0xFD;
    if (fp.available()){
      b = fp.read();
    }
    playBuf[fillPos] = b;
    fillPos++;
    if (fillPos==bufSize) fillPos=0;
    freeSz--;
  }
}

void prepareFile(char *fname){
  Serial.print("prepare [");
  Serial.print(fname);
  Serial.println("]...");
  
  fp = SD.open(fname);
  
  if (!fp){
    Serial.println("error opening music file");
    return;
  }  
    
  while (fp.available()) {
    byte b = fp.read();
    if (b==0xFF) break;
  }
 
  fillPos = 0;
  playPos = 0;
  cli();  
  fillBuffer();
  resetAY();
  sei();
}

File root;
int fileCnt = 0;

void openRandomFile(){
  int sel = random(0,fileCnt-1);

  Serial.print("File selection = ");
  Serial.print(sel, DEC);
  Serial.println();
  
  root.rewindDirectory();
  
  int i = 0;
  while (true) {
    File entry =  root.openNextFile();
    if (!entry)  break;

    Serial.print(entry.name());
    if (!entry.isDirectory()) {
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
      
      if (i==sel) prepareFile(entry.name());
      i++;
    }
    entry.close();
  }
}

void initFile(){
  Serial.print("Initializing SD card...");
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);

  if (!SD.begin(10)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  root = SD.open("/");

  // reset AY

  fileCnt = countDirectory(root);
  Serial.print("Files cnt = ");
  Serial.print(fileCnt, DEC);
  Serial.println();
  openRandomFile();

  Serial.print("Buffer size = ");
  Serial.print(bufSize, DEC);
  Serial.println();
  
  Serial.print("fillPos = ");
  Serial.print(fillPos, DEC);
  Serial.println();

  Serial.print("playPos = ");
  Serial.print(playPos, DEC);
  Serial.println();

  for (int i=0; i<bufSize;i++){
    Serial.print(playBuf[i],HEX);
    Serial.print("-");
    if (i%16==15) Serial.println();
  }

  Serial.println("done!");
}

int countDirectory(File dir) {
  int res = 0;
  root.rewindDirectory();
  while (true) {

    File entry =  dir.openNextFile();
    if (!entry)  break;

    Serial.print(entry.name());
    if (!entry.isDirectory()) {
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
      res++;
    }
    entry.close();
  }
  
  return res;
}

int skipCnt = 0;

ISR(TIMER1_COMPA_vect){
  if (skipCnt>0){
    skipCnt--;
  } else {
    int fillSz = 0;
    int freeSz = bufSize;
    if (fillPos>playPos) {
      fillSz = fillPos-playPos;
      freeSz = bufSize - fillSz;
    }
    if (playPos>fillPos) {
      freeSz = playPos - fillPos;
      fillSz = bufSize - freeSz;
    }

    boolean ok = false;
    int p = playPos;
    while (fillSz>0){
      byte b = playBuf[p];
      p++; if (p==bufSize) p=0;
      fillSz--;
      
      if (b==0xFF){ ok = true; break; }
      if (b==0xFD){ 
        ok = true; 
        playFinished = true;
        for (int i=0;i<16;i++) ay_out(i,0);
        break; 
      }
      if (b==0xFE){ 
        if (fillSz>0){
          skipCnt = playBuf[p];
          p++; if (p==bufSize) p=0;
          fillSz--;
          
          skipCnt = 4*skipCnt;
          ok = true; 
          break; 
        } 
      }
      if (b<=252){
        if (fillSz>0){
          byte v = playBuf[p];
          p++; if (p==bufSize) p=0;
          fillSz--;
          
          if (b<16) ay_out(b,v);
        } 
      }
    } // while (fillSz>0)
  
    if (ok){
      playPos = p;
    }
  } // else skipCnt 
}

Для полной автономности я ещё добавил усилитель на TDA2822M, сам проигрыватель потребляет 90 мА, вместе с усилителем — около 200 мА, при желании можно питать от аккумуляторов.



Обе макетки вместе:


Вот на этом этапе я пока остановился, музыку слушаю с макетки, раздумываю в каком корпусе я бы хотел это собрать. Думал подключить индикатор, но необходимости как-то не испытываю.

Реализация пока сыровата, т.к. устройство в состоянии разработки, но т.к. я могу его забросить на пару лет в таком состоянии, решил написать статью по горячим следам. Вопросы, предложения, замечания, исправления — приветствую в комментариях.

Использованная литература:

Комментарии (53)


  1. shtirlitsus
    07.04.2016 12:02
    +1

    Взрывозащищенное исполнение. Отлично!


  1. dlinyj
    07.04.2016 12:02

    Вот что-то подобное я очень хотел.

    Однако, опять же что-то сырое. Хочу автономный плеер. Тёплый и ламповый :)


    1. Z80A
      07.04.2016 12:15
      +1

      Сырое, но работает. Автономный, тёплый и ламповый, что не так-то?


      1. dlinyj
        07.04.2016 12:30

        Пока устройство не имеет корпуса и не используется, значит устройства нет. Я уже писал об этом.

        geektimes.ru/post/257212


        1. Z80A
          07.04.2016 12:39
          +1

          Спасибо, теперь понятно. Но я не разделяю вашу точку зрения.
          Я делаю корпус из того, что под руку придётся, это сильно экономит время.

          Вот например моё готовое устройство RFID-сканер:


          1. dlinyj
            07.04.2016 12:41

            Крутое устройство, респект. Что внутрях? Я тоже делал такую читалку.

            Не важно из чего и как делается. Важно, чтобы устройство было не на соплях на столе, а законченным.


            1. Z80A
              07.04.2016 12:50

              Arduino+RDM6300. Планировал копировщик, но случайно собрал на другой базе (в другом корпусе собран), поэтому пока остался ридером. Как скучно будет — допилю копирование.


              1. dlinyj
                07.04.2016 12:57

                У меня приятель сделал пассивный эмулятор таких меток на AVR.

                Можно было не вешать ещё одну платку, а сделать всё на АВР. Я делал.


                1. Z80A
                  07.04.2016 13:00

                  Поделишься исходниками твоего проекта на AVR? Я бы много времени сэкономил.


                  1. dlinyj
                    07.04.2016 13:04

                    Которого, эмуля или ридера? Ридер постараюсь найти, но с кучей переездом и прочим могли пролюбится. А вот эмуль найду.


                    1. Z80A
                      07.04.2016 13:07

                      Давай в личку, а то какой-то оффтоп пошёл?


                      1. dlinyj
                        07.04.2016 13:08

                        Может в этом офтопе кто-то найдёт для себя что-то полезное. После обеда всё скину сюда.


                        1. Z80A
                          07.04.2016 13:10

                          Хорошо. Софтовый ридер интереснее, чем эмуль, но кидай всё, что не жалко.


                          1. dlinyj
                            07.04.2016 13:34
                            +1

                            Вот сам эмулятор (то с чего мы начинали).

                            Сишный код
                            #include <avr/io.h>
                            #include <avr/sfr_defs.h>
                            #include <compat/ina90.h>
                            #include <avr/interrupt.h>
                            #include <stdlib.h>
                            #include <avr/sleep.h>
                            
                            #ifndef cbi
                            #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
                            #endif
                            #ifndef sbi
                            #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
                            #endif
                            
                            volatile int bit_counter=0;
                            volatile int byte_counter=0;
                            volatile int half=0;
                            
                            unsigned char data_card[5][8] = {{0xFF,0xC5,0x38,0x2D,0x19,0xD1,0xC9,0x5A},    //test
                                                            {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},     // other cards
                            				{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},    
                            				{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},    
                            				{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};    
                            
                            unsigned char data[8];
                            
                            void delay_ms(int ms) {              // 1.000 MHz
                              int i;
                              for (i=0; i < ms; i++) {
                            	TCNT0=0;
                            	while (TCNT0 <  125);
                              }
                            }
                            
                            ISR(SIG_OUTPUT_COMPARE1A){
                                TCNT1=0;
                            
                            //      if(byte_counter==0) sbi(PORTB,PD1);
                            //        else cbi(PORTB,PD1);
                            
                            	if (((data[byte_counter] << bit_counter)&0x80)==0x00) {
                            	    if (half==0) cbi(PORTB,PB1);
                            	    if (half==1) sbi(PORTB,PB1);
                            	}
                            	else {
                            	    if (half==0) sbi(PORTB,PB1);
                            	    if (half==1) cbi(PORTB,PB1);
                            	}
                                
                            	half++;
                            	if (half==2) {
                            	    half=0;
                            	    bit_counter++;
                            	    if (bit_counter==8) {
                            	        bit_counter=0;
                            	        byte_counter=(byte_counter+1)%8;
                            		}
                            	}
                            }
                            
                             
                            int main(void) {
                              int i;
                              DDRB=0xFF;
                              DDRC=0x00;
                              DDRD=0x00;
                              PORTC=0x00;
                            
                              TCCR0 = 2;             // timer0 clock/8 
                            //  TCCR1B = 2;		 // timer1 clock/8
                              TCCR1B = 3;
                            
                            //  OCR1A = 254;
                            //  OCR1A = 32;   //32           
                            
                              unsigned char sel = (PINC >> 1) & 0x07;
                              OCR1A = 30 + sel;
                              
                            //  for (i=0;i<8;i++) data[i]=data_card[sel][i];
                              for (i=0;i<8;i++) data[i]=data_card[0][i];
                              
                              sbi(TIMSK, OCIE1A);	 // timer1 Output Compare A match
                            
                              sei();
                            
                              while (1) {
                            
                              }
                                return 0;  
                            }
                            
                            


                          1. dlinyj
                            07.04.2016 13:40
                            +1

                            ww1.microchip.com/downloads/en/AppNotes/00680b.pdf — отличная пдф-ка о расчётах антенн.

                            Мой эмулятор выглядел вот так

                            image

                            Антенну считал и мотал к нему по мануалу. В ЖЖ можно найти описание как я это делал.

                            Собственно говоря делался по этому мануалу

                            Камрад из Германии скрестил эти два проекта и получил такую петрушку

                            image

                            Нашёл таки его сайт www.alexanderguthmann.de/en/emulator.html

                            Инфы море, поэтому можно пошерстить мой ЖЖ по тегу dlinyj.livejournal.com/tag/rfid (там много страниц).


                            1. dlinyj
                              07.04.2016 13:56
                              +2

                              Не ту пдф-ку скинул :(.
                              www.meraprojekt.com.pl/files/AN411.pdf вот она.

                              Больше всего доставляет картинка в документации



                          1. dlinyj
                            07.04.2016 13:41

                            Про ридер я нагнал :(


                            1. dlinyj
                              07.04.2016 13:45

                              Хотя вру, по моему вот этот пепелац, что на фото выше, может и читать и эмулировать


  1. Syzd
    07.04.2016 12:10

    «развязочный конденсатор по питанию — и работать стало в 100% случаев. Ладно, едем дальше…» поясните пожалйста схемой что сделали. А то тоже требовалось читать с SD карты. Вдруг решит проблему.


    1. Z80A
      07.04.2016 12:26
      +1

      Все выводы питания SD-карты и ардуино собрал как можно ближе друг к другу и в этом месте поставил конденсатор 10 микрофарад:


      1. dlinyj
        07.04.2016 12:31

        Ещё хорошо поставить керамику 0,1 мкФ


        1. Z80A
          07.04.2016 12:32
          +1

          Если посмотреть на фото макетки, то она там стоит чуть левее, ближе к сопру, чтобы по звуку было меньше помех.


          1. dlinyj
            07.04.2016 12:35

            Я просто комментатору отметил, чтобы он имел это в виду.

            Когда я был мелкий, то экономил на кондёрах по питанию. В результате всегда имел много весёлых граблей :)))


            1. Z80A
              07.04.2016 12:56
              +1

              Просто натыкать 0.1 не всегда полезно, а иногда и даже вредно. Зависит от спектра помех и резонансных частот конденсаторов. Вот хорошая статья: О развязке питания с примерами с интересными комментариями.


              1. dlinyj
                07.04.2016 13:02

                Я уже и забыл про него :)


      1. Syzd
        08.04.2016 07:47

        между плюсом и минусом?


  1. Mr_Destiny
    07.04.2016 13:54

    Простите, я наверно слишком молод и не понимаю классики (никогда не было спектрума), но почему нельзя просто перегнать мелодии в mp3 и слушать с буквально чего угодно? Этому есть какие-то обьективные препятствия кроме «тёплый ламповый звук»?

    Пожалуйста, поймите меня правильно — мне действительно интересно зачем нужно городить огород вокруг динамика?


    1. dlinyj
      07.04.2016 13:58
      +1

      Это из разряда почему люди слушают музыку на ламповых усилителях, а не ставят программный звуковой фильтр «лампы» ;)


      1. Mr_Destiny
        07.04.2016 14:00
        +1

        Я так и подумал. А разве ардуино не разрушает «ламповость» звука? хД


        1. dlinyj
          07.04.2016 14:02
          +1

          Тут важен оригинальный звуковой чип, который собственно говоря и используется.


          1. ice2heart
            07.04.2016 14:06
            -3

            А разве lossless формат не дает звук лучшего качества чем может выдать чип?
            Или это как лампы подсвеченные светодиодами дающие к окраске звука?


            1. dlinyj
              07.04.2016 14:16
              +1

              Мне кажется вы не понимаете механизм трекерной музыки, раз задаёте такие вопросы.

              Если вы хотите потроллить автора поста, то для начала разберитесь в матчасти.


              1. ice2heart
                07.04.2016 14:27
                +1

                Я понимаю как работает трекерная музыка. Я не собираюсь тролить автора. Мне правда интересно есть ли разница между звучанием чипа в «живую» или записанного с него звука в lossless(она конечно будет, но будет ли она заметна). Понятно что трекерный файлик будет во много раз меньше чем lossless, но устройств которые могут его проиграть намного больше.


                1. dlinyj
                  07.04.2016 14:40

                  Тут мы упираемся в другую проблему — это перегонку звука. Вопрос в том, как сделать её наиболее тёплой и ламповой? А главное целессобразности этой перегонки, если для её осуществления надо делать такую же приблуду. => проще использовать данную приблуду просто из коробки, не морочась с форматами


                  1. vvzvlad
                    08.04.2016 16:05

                    Подключить к выходу этой макетки любой комп с нормальной звуковой картой и писать в lossless. Дальше загнать на любой плеер и наслаждаться. Это, если что, не руководство к действию, а просто альтернатива «спять плеер наполовину, забросить его на два года»


                    1. dlinyj
                      11.04.2016 11:08
                      -1

                      Спасибо большое Капитан Очевидность. Выше я как раз об этом говорил.


            1. VaalKIA
              07.04.2016 15:44

              >А разве lossless формат не дает звук лучшего качества чем может выдать чип?
              Ещё совсем недавно люди, собирая компьютер, выбирали звуковую карту, и в многочисленных обзорах сравнивали всякие MIDI банки и их звучание… как сейчас помню AWE32 и т.п. Типа, если вы хотите насладиться настоящим качеством музыки в вашей любимой игре выбирайте супер мега карту и получите оркестровое качество. С чего бы это, если везде одна и та же цифра (и речь тут не о тёплых ламповых проводах из бумаги)?
              Продолжаем тему: формат MOD, вот чем он отличается от Wav, к примеру?
              Более современное: Csound http://csound.github.io/documentation.html
              Ну а теперь переходим к чиповой музыке и, внезапно, оказывается, что это вовсе не музыка, а программа для чипа и в общем случае, мы можем только логи писать и там где было 100 байт программы, у нас запишутся мегабайты логов. Потом смотрим на параметры чипа, и выясняется ещё и что 44КГц мало, что бы эмулировать по этим логам вывод чипа и один в один всё равно не получится, то есть будет играть огромную роль, качество софта, осуществляющего приближение, то есть речь, о LossLess здесь уже не идёт, но да — можно очень точно воспроизвести этот раздутый лог.


              1. Syzd
                08.04.2016 07:50

                Ну сейчас ясное дело цифра, хотя и с разной частотой дискритизации. А тогда если в игре были не mp3, а midi то можно было слышать или пластмассовое детское пианино или сэмплы реального. Хотя и та и та цифровая музыка.


    1. Z80A
      07.04.2016 14:30
      +2

      Этот вопрос можно задать большинству DIY разработок.
      У меня два ответа, серьёзный и не очень, но гораздо ближе к правде:

      1. Получить полезный навык и показать другим интересный несложный проект.
      2. Мне так захотелось.


      1. Mr_Destiny
        07.04.2016 14:35
        +1

        Да я ж не в претензии — я просто не знал что используется оригинальный звуковой чип (к стати, совсем оригинальный, или «точно такой-же»?), как указал dlinyj

        И оффтоп: как мне кажется, мотивация «мне так захотелось» намного более полезная чем все остальное. Разумеется, когда речь идет о чем-то более конструктивном чем тупое лежание на диване


        1. Z80A
          07.04.2016 15:10

          Чип самый что ни на есть оригинальный.
          Причём рядом лежит его собрат — YM2149F, который подключается похожим образом, но имеет чуть более другие звуковые оттенки, из-за которых было немало «тёплых ламповых срачей». Вот тоже буду сравнивать.


          1. dlinyj
            07.04.2016 15:21

            Вообще, я бы послушал вживую. Не могу сказать, что фанат, но интересно


            1. Z80A
              07.04.2016 15:37
              +1

              Вот теперь жди пока я в корпусе их оба не соберу.
              Тебе же нельзя без корпуса :-)


              1. makaroff
                07.04.2016 15:43
                +1

                Вот у меня тоже уже больше года лежит YM2149F купленный наебэй под ЛПТ-проигрыватель… Готов посмотреть на вашу реализацию ;) Ардуинов и шилдов в запасах ну просто завались!


                1. dlinyj
                  07.04.2016 15:46

                  Где искать и по чём? Под линуксом интересно работает?


                1. Z80A
                  07.04.2016 15:59
                  +1

                  Так что там смотреть — просто подключаете YM вместо AY и вперёд, номера выводов другие, а программирование точно такое же.

                  Я вот думаю их подключить одновременно параллельно, один в левое, другой в правое ухо — тогда может и усышу разницу.


              1. dlinyj
                07.04.2016 15:47

                Тролинг засчитан


  1. hockfan
    07.04.2016 15:53
    +2

    Я, возможно, слепой, но не нашел способа написать вам в личку. Есть такой замечательных архив музыки как zxtunes dot com. Вам должно понравиться.


    1. Z80A
      07.04.2016 15:56

      Спасибо, я знаю про этот сайт. Так как у меня громадный архив трекерной музыки, то там ничего нового я не увидел, но мне очень понравились ремиксы, особенно Putzi — Crazy Comets и Saanvi — Ramparts!


      1. hockfan
        07.04.2016 16:01

        Хорошо. Просто авторы этого сайта стучались ко многим авторам персонально и многое из музыки на этом сайте никогда нигде не публиковалось. А ремиксы я не замечал. Действительно очень круто!


  1. Tabke
    08.04.2016 00:17
    +1

    Повторил на Yamaha YM2149. Всё работает.
    Спасибо автору за интересную статью.

    Скрытый текст


    1. Z80A
      08.04.2016 01:16

      Спасибо что написали об этом. Приятно знать, что не зря потратил время на написание статьи.


      1. wirbi
        11.04.2016 12:47

        Повторил на YM2149F пока на макетке, но в девайс обязательно оформлю. Доволен как слон. Спасибо за статью.