image

Привет, Geektimes! Вам было когда нибудь интересно, как «звучит» электрический сигнал, проходящий по дорожкам печатных плат между микросхем, транзисторов, диодов, резисторов и конденсаторов? Один из вариантов такого сигнала в современной электронике — цифровая шина, а один из популярных интерфейсов для обмена данных по шине — UART. Он часто используется в микроконтроллерах для связи с компьютером или какой либо переферией. Чтобы получить звук на шине, вовсе не обязательно подключать динамик с усилителем к реальной шине с UARTом, ведь ее можно симулировать в программе. Вам интересно, какие звуки в итоге получились, или нужна программа, чтобы самому поэкспериментировать? Тогда прошу под кат.

Слушаем файлы на шине с UART


Какой звук получится, если передавать файлы через UART? Вот некоторые примеры, полученные при следующих параметрах UART:

  • Скорость 115200 бод
  • Разрядность 8 бит
  • Бит проверки на четность: отсутствует
  • Длина стоп бита: 1

Звук игры Сталкер Тень Чернобыля (файл XR_3DA.exe, в самом конце трека, начиная с 2:36, есть мелодия).

Звук текста и кода статьи про синтезатор речи (сама статья тут).

Как звучит фотография «Лены»?

image

Получился просто шум.

Звук книги «Энтропия и прогноз времянных рядов в теории динамических систем» в pdf формате.

Звук прошивки микроконтроллера серии Atmega для wav плеера.

Для чего это можно использовать?


Теоретически, может быть такая информация в виде текста, или картинки, или видео, или в виде программы, которая будет иметь не только функциональный смысл или эстетическую значимость, но также и «красивый» звук цифровой шины, тогда это получается некая «цифровая» поэзия. Также можно разнообразить сэмплы для дабстепа Вообще, на мой взгляд, слушать звуки цифровой шины примерно так же интересно, как слушать шум радиоволн на коротких волнах, в общем на любителя.

Как это работает или немного про UART


Что такое UART можно почитать на википедии. UART очень легко симулировать в программе. По сути необходимо лишь уметь создавать перепад сигнала от 0 до 1 и обратно (в случае WAV файла с разрядностью 16 бит, это значения от -A до +A, где A это ампилутда сигнала) и записывать его в звуковой файл. Интерфейс UART функционирует примерно так: после стартового бита, который равен логическому «нулю», нужно выставлять уровень в зависимости от предоставленных данных, от младшего бита к старшему. Далее идет бит четности, который можно не использовать. Под конец сообщения ставится стоп бит (логическая «единица»), длина которого может быть разной. Пример кода можно посмотреть в исходниках, которые есть в конце статьи. Подробнее про UART можно посмотреть в сети, материала очень много. UART можно использовать не по назначению, например как ШИМ, а в нашем случае это означает, что теоретически можно даже передать полноценный звуковой сигнал сразу на динамик, как это делается в wav плеерах на микроконтроллере. Однако, я скорее предлагаю использовать его как генератор меандра. Частоту тона и фазу сигнала при этом можно предоставлять в виде бит данных, например 00001111 создаст меандр, период которого будет равен 10-ти периодам времени передачи одного бита (так как в данном случае есть еще стартовый бит, равный 0, и стоповый бит, равный 1). Из-за стартового и стопового бита не все периоды меандров получится передать, например в таком случае 01100110, так как по сути мы будем слушать такую последовательность на шине 0011001101. Если использовать высокую скорость передачи данных, например 115200 бод, то имеет смысл создавать слышимые звуковые частоты, растянув периоды меадров на несколько байт.

...


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

Исходный код программы предоставлен ниже:

Заголовочный файл SoundsDigitalBus.h
#ifndef SOUNDS_DIGITAL_BUS_H_INCLUDED
#define SOUNDS_DIGITAL_BUS_H_INCLUDED

#define SDB_WAV_FILE_NAME "sdb_output.wav"
#define SDB_UART_BIT 8
#define SDB_UART_PARITY 0
#define SDB_UART_STOP_BIT 1
#define SDB_UART_BAUDRATE 9600
#define SDB_UART_BAUDRATE_MAX 921600

/// максимальное значение (амплитуда) сэмпла аудио
#define SDB_MAX_DATA 30000
/// каналов в аудио
#define SDB_CANNEL 1
/// кол-во бит (разрешение) аудио
#define SDB_BIT 16
/// частота дискретизации аудио
#define SDB_FREQUENCY 96000
/// Количество буферов в очереди OpenAL
#define OPENAL_NUM_OF_DYNBUF 32
/// Настройки OpenAL
#define SDB_OPENAL_BIT SDB_BIT
#define SDB_OPENAL_CANNEL SDB_CANNEL
#define SDB_OPENAL_FREQUENCY SDB_FREQUENCY
#define SDB_OPENAL_FORMAT AL_FORMAT_MONO16
/// Количество байт в буфере
#define SDB_BUFFER_MAX 4800


/// использовать OpenAL для вывода аудио (если Да, то выставить 1)
#define SDB_WITH_OPENAL 1
/// режим отладки (если Да, то выставить 1)
#define SDB_WITH_DEBUG_MODE 0

#if SDB_WITH_OPENAL == 1
// Подключение библиотеки OpenAL Для вывода звука
#include <openal/al.h>
#include <openal/alc.h>
#endif

#if SDB_WITH_DEBUG_MODE == 1
// Для режима отладки
#include <stdio.h>
#include <locale.h>
#endif

//для работы с файлами
#include <stdio.h>
//для работы со строками
#include <string.h>



class sdb {
private:
#if SDB_WITH_OPENAL == 1
    // работа с openAl взята из библиотеки
    // синтезатора речи speesy
    ALCdevice* openAlDevice;
    ALCcontext* openAlContext;
    ALuint openAlSource;
    signed char openAlnBuf; //количество буферов
#endif
    // ----------------------------------------
    // для записи в WAV файл
    FILE *fpSave;
    unsigned short wavBlockAlign;
    unsigned long wavSubchunk2Size;
    unsigned long wavChunkSize;
    unsigned char wavLenDataType;
    // ----------------------------------------
    // для генерирования звуков шин данных
    double dTime; // длина времени одного сэмпла, в с.
    double allTime; // общее время трека
    short busState; // состояние шины (уровень сигнала)
    short busDataOne[SDB_BUFFER_MAX]; // буферы, куда пишем wav
    short busDataTwo[SDB_BUFFER_MAX];
    unsigned char switchBuffer; // переключатель между буферами
    unsigned int posBufferOne, posBufferTwo; // позиция в буферах
    unsigned int posAllBuffer; // общая позиция
    char wavFileName[512]; // имя wav файла
    char isCreateWavFileFlag; // флаг, что wav файл был создан
    char isBufferOneFlag; // флаг, что готов первый буфер
    char isBufferTwoFlag;
    unsigned int uartBaudrate; // скорость UART в бод
    unsigned int uartT;
    unsigned char uartBit; // количество бит
    unsigned char uartStopBit; // количество стоп бит
    unsigned char uartParityBit;
    unsigned char isAudioOutput;
    unsigned char isWavFileOutput;
#if SDB_WITH_OPENAL == 1
    ALboolean CheckALCError(void);
    ALboolean CheckALError(void);
    char initOpenAL(void);
    void destroyOpenAL(void);
    void playOpenAlSound(void);
    void stopOpenAlSound(void);
    void closeOpenAlSound(void);
    int getBufferStatusOpenAl(void);
    void setBufferOpenAl(signed short *buf,unsigned long siz);
    char updateOpenAl(void);
#endif
    char createWavFile(char * filename,unsigned long sampleRate,unsigned short bitsPerSample, unsigned short numChannels);
    void writeSampleWavFile(void *data);
    void writeDataBlockWavFile(void *data,unsigned long len);
    void closeWavFile(void);
    void busDelay(unsigned short us);

public:
    sdb(void);
    ~sdb(void);
    /** @brief функция генерирует звук одного байта шины 1-wire
        @param[in] data байт для отправки по 1-wire
    */
    void oneWireSendByte(unsigned char data);
    /** @brief генерирует звук сброса шины 1-wire
    */
    void oneWireReset(void);

    /** @brief останаливает работу шины 1- wire
    */
    void oneWireStop(void);

    /** @brief передает один байт по шине UART
        @param[in] data один символ шины UART
    */
    void uartSendByte(unsigned char data);

    /** @brief передает данные по шине UART
        @param[in] data один символ шины UART
    */
    void uartSend(unsigned long data);

    /** @brief устанавливает скорость UART
        @param[in] baudrate скорость шины UART
    */
    void uartSetBaudrate(unsigned long baudrate);

    /** @brief устанавливает количество отправляемых бит
        @param[in] bit количество бит, отправляемых шиной UART за один раз
    */
    void uartSetBit(unsigned char bit);

    /** @brief устанавливает количество стоп битов
        Данная функция устаналивает количество стоп битов.
        Минимальное значение стоп бита 1.
        @param[in] bit количество стоп битов
    */
    void uartSetStopBit(unsigned char bit);

    /** @brief устанавливает бит четности
        Данная функция устанавливает флаг использования в UART бита четности.
        Если передать 1, то бит четности будет задействован, если передать 0, то
        передача данных по UART будет идти без бита четности.
        @param[in] state флаг, включающий бит четности.
    */
    void uartSetParityBit(unsigned char state);

    /** @brief функция останавливает передачу UART
        Данная функция завершает работу UART, сохраняет или воспроизводит оставшиеся байты.
        Функцию вызывать обязательно в конце передачи данных по шине.
    */
    void uartStop(void);

    /** @brief функция устанавливает имя wav файла
        @param[in] filename имя wav файла
    */
    void setWavFileName(char* filename);

    /** @brief включить вывод аудио через OpenAL
    */
    void playAudioOn(void);

    /** @brief выключить вывод аудио через OpenAL
    */
    void playAudioOff(void);

    /** @brief включить запись аудио в wav файл
    */
    void recordOn(void);

    /** @brief выключить запись аудио в wav файл
    */
    void recordOff(void);
};


#endif // MUSICDIGITALBUS_H_INCLUDED


Исходный код файла SoundsDigitalBus.cpp
#include "SoundsDigitalBus.h"
#if SDB_WITH_OPENAL == 1
    // обработчики ошибок
    ALboolean sdb::CheckALCError(void) {
        ALenum ErrCode;
        ErrCode = alcGetError(openAlDevice);
        if (ErrCode != ALC_NO_ERROR) {
            return AL_FALSE;
        }
        return AL_TRUE;
    }
    ALboolean sdb::CheckALError(void) {
        ALenum ErrCode;
        if ((ErrCode = alGetError()) != AL_NO_ERROR) {
            return AL_FALSE;
        }
        return AL_TRUE;
    }

    // инициализация OpenAL
    char sdb::initOpenAL(void) {
        ALfloat SourcePos[] = {0.0, 0.0, 0.0};
        ALfloat SourceVel[] = {0.0, 0.0, 0.0};
        // Позиция слушателя.
        ALfloat ListenerPos[] = { 0.0, 0.0, 0.0 };
        // Скорость слушателя.
        ALfloat ListenerVel[] = { 0.0, 0.0, 0.0 };
        // Ориентация слушателя. (Первые 3 элемента – направление «на», последние 3 – «вверх»)
        ALfloat ListenerOri[] = { 0.0, 0.0, -1.0,  0.0, 1.0, 0.0 };
        #if SDB_WITH_DEBUG_MODE == 1
        printf("alcOpenDevice\n");
        #endif

        openAlDevice = alcOpenDevice(0); // open default device
        if (openAlDevice != 0) {
            openAlContext = alcCreateContext(openAlDevice,0); // create context
            if (openAlContext != 0) {
                #if SDB_WITH_DEBUG_MODE == 1
                printf("alcMakeContextCurrent\n");
                #endif
                alcMakeContextCurrent(openAlContext); // set active context
            } else  {
                #if SDB_WITH_DEBUG_MODE == 1
                printf("Error context\n");
                #endif
                return 0;
            }
        } else {
            #if SDB_WITH_DEBUG_MODE == 1
            printf("Error Open Device\n");
            #endif
            return 0;
        }

        // Позиция
        alListenerfv(AL_POSITION,    ListenerPos);
        // Скорость
        alListenerfv(AL_VELOCITY,    ListenerVel);
        // Ориентация
        alListenerfv(AL_ORIENTATION, ListenerOri);

        alGenSources(1, &openAlSource);
        if (!CheckALError())
            return false;

        alSourcef (openAlSource, AL_PITCH,    1.0f);
        alSourcef (openAlSource, AL_GAIN,    1.0f);
        alSourcefv(openAlSource, AL_POSITION,  SourcePos);
        alSourcefv(openAlSource, AL_VELOCITY,  SourceVel);
        alSourcei (openAlSource, AL_LOOPING,  AL_FALSE);

        alSourcei(openAlSource, AL_LOOPING, AL_FALSE);
        openAlnBuf = 0;
        return 1;
    }

    void sdb::destroyOpenAL(void) {
        alSourceStop(openAlSource);
        // Выключаем текущий контекст
        alcMakeContextCurrent(0);
        // Уничтожаем контекст
        alcDestroyContext(openAlContext);
        // Закрываем звуковое устройство
        alcCloseDevice(openAlDevice);
    }

    void sdb::playOpenAlSound(void) {
        alSourcePlay(openAlSource);
    }

    void sdb::stopOpenAlSound(void) {
        alSourceStop(openAlSource);
    }

    void sdb::closeOpenAlSound(void) {
        alSourceStop(openAlSource);
        if (alIsSource(openAlSource))
            alDeleteSources(1, &openAlSource);
    }

    int sdb::getBufferStatusOpenAl(void) {
        int processed = 0;
        if (openAlnBuf == 0)
            return 1;
        alGetSourcei(openAlSource, AL_BUFFERS_PROCESSED, &processed);
        CheckALError();
        #if SDB_WITH_DEBUG_MODE == 1
            printf("getBufferStatus: %d\n",processed);
        #endif
        if (processed != 0) {
            return processed;
        }
        return 0;
    }

    void sdb::setBufferOpenAl(signed short* buf, unsigned long siz) {
        int processed = 0;
        ALuint BufID = 0;
        #if _OPENAL_FORMAT == AL_FORMAT_MONO16
        siz = siz*2;
        #endif // _OPENAL_FORMAT
        #if _OPENAL_FORMAT == AL_FORMAT_STEREO16
        siz = siz*4;
        #endif // _OPENAL_FORMAT
        #if _OPENAL_FORMAT == AL_FORMAT_STEREO8
        siz = siz*2;
        #endif // _OPENAL_FORMAT
        // Получаем количество отработанных буферов
        alGetSourcei(openAlSource, AL_BUFFERS_PROCESSED, &processed);
        CheckALError();
        //если отработанных буферов нет, есть новые данные и число буферов не превысило максимума
        if ((processed == 0) && (openAlnBuf < OPENAL_NUM_OF_DYNBUF)) {
            openAlnBuf++; //увеличиваем число буферов
            alGenBuffers(1, &BufID); //создаем новый буфер
            alBufferData(BufID,SDB_OPENAL_FORMAT,buf,siz,SDB_OPENAL_FREQUENCY); //загружаем данные в новый буфер
            alSourceQueueBuffers(openAlSource, 1, &BufID); //добавляем буфер в очередь
            if (openAlnBuf == 1)
                alSourcePlay(openAlSource);
        } else {
            #if SDB_WITH_DEBUG_MODE == 1
                printf("processed: %d openAlnBuf: %d\n",processed,openAlnBuf);
            #endif
            // ждем, когда будет обработан хотя бы один буфер
            while (getBufferStatusOpenAl() == 0);
            // убираем из очереди буфер
            alSourceUnqueueBuffers(openAlSource, 1, &BufID);
            CheckALError();
            // загружаем новый буфер
            alBufferData(BufID,SDB_OPENAL_FORMAT,buf,siz,SDB_OPENAL_FREQUENCY);
            CheckALError();
            alSourceQueueBuffers(openAlSource, 1, &BufID);
            CheckALError();
        }
    }
    // функция очищяет все буферы по мере их опустошения, и останавливает проигрывание
    // возвращает 1 если еще есть не пустые буферы
    char sdb::updateOpenAl(void) {
        int processed = 0;
        ALuint      BufID;
        // Получаем количество отработанных буферов
        alGetSourcei(openAlSource, AL_BUFFERS_PROCESSED, &processed);
        #if SDB_WITH_DEBUG_MODE == 1
            printf("updateOpenAl: %d\n",processed);
        #endif
        // если обработаны все буферы
        if (openAlnBuf == processed) {
            // Если таковые существуют то
            while (processed--) {
                // Исключаем их из очереди
                alSourceUnqueueBuffers(openAlSource, 1, &BufID);
                if (!CheckALError())
                    return 0;
                alDeleteBuffers(1, &BufID);
                openAlnBuf--;
            }
            alSourceStop(openAlSource);
            #if SDB_WITH_DEBUG_MODE == 1
                printf("alSourceStop: %d\n",openAlnBuf);
            #endif
            return 0;
        }
        return 1;
    }
#endif

    // задержка для шин данных. тут генерируется звук
    void sdb::busDelay(unsigned short us) {
        double Time = (double)us/1000000.0;
        double locTime = allTime;
        char isFlag = 0;
        // создаем wav файл, если он не был создан ранее
        if (isCreateWavFileFlag == 0) {
            if (isWavFileOutput == 1) {
                isFlag = createWavFile(wavFileName,SDB_FREQUENCY,SDB_BIT,SDB_CANNEL);
                // если файл был успешно открыт, то ставим флаг
                if (isFlag == 1)
                    isCreateWavFileFlag = 1;
            }
            if (isAudioOutput == 1) {
                initOpenAL();
                if (isWavFileOutput == 0)
                    isCreateWavFileFlag = 1;
            }
        }
        allTime = allTime + Time;
        // если файл был открыт
        if (isCreateWavFileFlag == 1)
            // начинаем делать сэмплы аудиоданных
            while(locTime < allTime) {
                if (switchBuffer == 0) {
                    if (posBufferOne >= SDB_BUFFER_MAX) {
                        posBufferOne = 0;
                        posBufferTwo = 0;
                        busDataTwo[posBufferTwo++] = busState;
                        isBufferOneFlag = 1;
                        switchBuffer = 1;
                        if (isWavFileOutput == 1)
                            writeDataBlockWavFile(busDataOne,SDB_BUFFER_MAX);
                        #if SDB_WITH_OPENAL == 1
                            if (isAudioOutput == 1)
                                setBufferOpenAl(busDataOne,SDB_BUFFER_MAX);
                        #endif
                    } else {
                        busDataOne[posBufferOne++] = busState;
                    }
                } else
                if (switchBuffer == 1) {
                    if (posBufferTwo >= SDB_BUFFER_MAX) {
                        posBufferOne = 0;
                        posBufferTwo = 0;
                        busDataOne[posBufferOne++] = busState;
                        isBufferTwoFlag = 1;
                        switchBuffer = 0;
                        if (isWavFileOutput == 1)
                            writeDataBlockWavFile(busDataTwo,SDB_BUFFER_MAX);
                        #if SDB_WITH_OPENAL == 1
                            if (isAudioOutput == 1)
                                setBufferOpenAl(busDataTwo,SDB_BUFFER_MAX);
                        #endif
                    } else {
                        busDataTwo[posBufferTwo++] = busState;
                    }
                }
                posAllBuffer++;
                locTime = locTime + dTime;
            }
    }


    char sdb::createWavFile(char * filename,unsigned long sampleRate,unsigned short bitsPerSample, unsigned short numChannels) {
        char type[4];
        const unsigned long subchunk1Size = 16;
        unsigned long byteRate;
        const unsigned short audioFormat = 1;
        unsigned short len_str = 0;
        char str_filename[512] = {0};
        unsigned short i;
        // количество байт в одном сэмле одного канала
        wavLenDataType = bitsPerSample/8;

        wavSubchunk2Size = 0;
        wavChunkSize = wavSubchunk2Size + 44 - 8;
        // Количество байт на одну выборку
        wavBlockAlign = bitsPerSample / (8 * numChannels);
        //Количество байт, переданных за секунду воспроизведения.
        byteRate = sampleRate * wavBlockAlign;

        strcpy(str_filename,filename);
        len_str = strlen(str_filename);
        if (len_str < 4)
            return 0;
        // проверка имени файла на наличие расширения .wav
        i = 0;
        while(i < len_str) {
            if (filename[i] == '.' && (i + 3) < len_str) {
                if (((filename[i + 1] == 'w')
                    && (filename[i + 2] == 'a')
                    && (filename[i + 3] == 'v'))
                    ||
                    ((filename[i + 1] == 'W')
                    && (filename[i + 2] == 'A')
                    && (filename[i + 3] == 'V'))) {
                    // если имя имеет расширение wav
                    break;
                } else {
                    if ((i + 3) >= 512)
                        return 0;
                    filename[i + 1] = 'w';
                    filename[i + 2] = 'a';
                    filename[i + 3] = 'v';
                    len_str = i + 4;
                    break;
                }
            } else
            if ((i + 1) == len_str) {
                if ((i + 3) >= 512)
                    return 0;
                filename[i + 1] = '.';
                filename[i + 2] = 'w';
                filename[i + 3] = 'a';
                filename[i + 4] = 'v';
                len_str = i + 5;
                break;
            }
            i++;
        }

        type[0] = filename[len_str - 4];
        type[1] = filename[len_str - 3];
        type[2] = filename[len_str - 2];
        type[3] = filename[len_str - 1];
        if (type[0]!='.'||type[1]!='w'||type[2]!='a'||type[3]!='v') {
            if (type[0]!='.'||type[1]!='W'||type[2]!='A'||type[3]!='V') {
                return 0;
            }
        }
        fpSave=fopen(str_filename,"wb");
        type[0]='R';
        type[1]='I';
        type[2]='F';
        type[3]='F';
        fwrite(&type,sizeof(char),4,fpSave);
        fwrite(&wavChunkSize,sizeof(unsigned long),1,fpSave);

        type[0]='W';
        type[1]='A';
        type[2]='V';
        type[3]='E';
        fwrite(&type,sizeof(char),4,fpSave);

        type[0]='f';
        type[1]='m';
        type[2]='t';
        type[3]=' ';
        fwrite(&type,sizeof(char),4,fpSave);
        fwrite(&subchunk1Size,sizeof(unsigned long),1,fpSave);
        fwrite(&audioFormat,sizeof(unsigned short),1,fpSave);
        fwrite(&numChannels,sizeof(unsigned short),1,fpSave);
        fwrite(&sampleRate,sizeof(unsigned long),1,fpSave);
        fwrite(&byteRate,sizeof(unsigned long),1,fpSave);
        fwrite(&wavBlockAlign,sizeof(unsigned short),1,fpSave);
        // Количество бит в сэмпле. Так называемая “глубина” или точность звучания. 8 бит, 16 бит и т.д.
        fwrite(&bitsPerSample,sizeof(unsigned short),1,fpSave);

        type[0]='d';
        type[1]='a';
        type[2]='t';
        type[3]='a';
        // subchunk2Id
        // Содержит символы “data” (0x64617461 в big-endian представлении)
        fwrite(&type, sizeof(char), 4,fpSave);
        wavSubchunk2Size = 0;
        //Количество байт в области данных.
        fwrite(&wavSubchunk2Size, sizeof(unsigned long), 1,fpSave);
        return 1;
    }

    void sdb::writeSampleWavFile(void* data) {
        fwrite(data, wavLenDataType, wavBlockAlign, fpSave);
        wavSubchunk2Size = wavSubchunk2Size + wavLenDataType*wavBlockAlign;
    }

    void sdb::writeDataBlockWavFile(void* data, unsigned long len) {
        fwrite(data, wavLenDataType, len, fpSave);
        wavSubchunk2Size = wavSubchunk2Size + len*wavLenDataType;
    }
    // данная функция закрывает аудиофайл и записывает количесвто байт данных.
    void sdb::closeWavFile(void) {
        wavChunkSize = wavSubchunk2Size + 44 - 8;
        fseek(fpSave,4,SEEK_SET);
        fwrite(&wavChunkSize,4,1,fpSave);
        fseek(fpSave,40,SEEK_SET);
        fwrite(&wavSubchunk2Size,4,1,fpSave);
        fclose(fpSave);
    }

    // конструктор
    sdb::sdb(void) {
        openAlnBuf = 0;
        wavBlockAlign = 0;
        wavSubchunk2Size = 0;
        wavChunkSize = 0;
        wavLenDataType = 0;
        fpSave = NULL;
        strcat(wavFileName,SDB_WAV_FILE_NAME);
        dTime = 1.0/(double)SDB_OPENAL_FREQUENCY;
        allTime = 0.0;
        // переключатель буферов на первом буфере
        switchBuffer = 0;
        // позиция в буферах (сумма)
        posAllBuffer = 0;
        // позиции в буфере обнуляем
        posBufferOne = 0;
        posBufferTwo = 0;
        isBufferOneFlag = 0;
        isBufferTwoFlag = 0;
        isCreateWavFileFlag = 0;
        busState = SDB_MAX_DATA;
        uartSetBaudrate(SDB_UART_BAUDRATE);
        uartSetBit(SDB_UART_BIT);
        uartSetStopBit(SDB_UART_STOP_BIT);
        uartSetParityBit(SDB_UART_PARITY);
        recordOn();
        playAudioOn();
    }

    // деструктор
    sdb::~sdb() {
        if (isCreateWavFileFlag == 1) {
            if (posBufferOne > 0) {
                if (isWavFileOutput == 1)
                    writeDataBlockWavFile(busDataOne,posBufferOne);
                #if SDB_WITH_OPENAL == 1
                    if (isAudioOutput == 1)
                        setBufferOpenAl(busDataOne,posBufferTwo);
                #endif

            } else
            if (posBufferTwo > 0) {
                if (isWavFileOutput == 1)
                    writeDataBlockWavFile(busDataTwo,posBufferTwo);
                #if SDB_WITH_OPENAL == 1
                    if (isAudioOutput == 1)
                        setBufferOpenAl(busDataTwo,posBufferTwo);
                #endif
            }
            if (isWavFileOutput == 1)
                closeWavFile();
            isCreateWavFileFlag = 0;
            #if SDB_WITH_OPENAL == 1
                if (isAudioOutput == 1) {
                    while (1) {
                        // Ждем, когда звук проиграет до конца
                        if (updateOpenAl() == 0)
                            break;
                    }
                    closeOpenAlSound();
                    destroyOpenAL();
                }
            #endif
        }
    }

    // функция передает данные по шине one wire
    void sdb::oneWireSendByte(unsigned char data) {
        for (register unsigned char i = 0; i < 8; i++) {
            if((data & (1 << i)) == 1 << i) {
                busState = 0;
                busDelay(12);
                busState = SDB_MAX_DATA;
                busDelay(65);
            } else {
                busState = 0;
                busDelay(65);
                busState = SDB_MAX_DATA;
                busDelay(12);
            }
        }
        busState = SDB_MAX_DATA;
    }

    // функция передает один байт по шине uart
    void sdb::uartSendByte(unsigned char data) {
        unsigned short pBit = 0; // переменная для бита четности
        // старт бит
        busState = SDB_MAX_DATA;
        busDelay(uartT);
        busState = -SDB_MAX_DATA;
        // данные
        for (register unsigned char i = 0; i < 8; i++) {
            if((data & (1<<i)) == 1<<i) {
                busState = -SDB_MAX_DATA;
                busDelay(uartT);
                busState = -SDB_MAX_DATA;
                pBit++;
            } else {
                busState = SDB_MAX_DATA;
                busDelay(uartT);
                busState = -SDB_MAX_DATA;
            }
        }
        // бит честности
        if (uartParityBit != 0) {
            if ((pBit & 0x0001) == 0) {
                busState = -SDB_MAX_DATA;
                busDelay(uartT);
                busState = -SDB_MAX_DATA;
            } else {
                busState = SDB_MAX_DATA;
                busDelay(uartT);
                busState = -SDB_MAX_DATA;
            }
        }
        // стоп бит
        busState = -SDB_MAX_DATA;
        for (register unsigned char i = 0; i < uartStopBit; i++)
            busDelay(uartT);
        busState = -SDB_MAX_DATA;
    }

    // функция передает данные по шине uart
    void sdb::uartSend(unsigned long data) {
        unsigned short pBit = 0; // переменная для бита четности
        // старт бит
        busState = SDB_MAX_DATA;
        busDelay(uartT);
        busState = -SDB_MAX_DATA;
        // данные
        for (register unsigned char i = 0; i < uartBit; i++) {
            if((data & (1<<i)) == 1<<i) {
                busState = -SDB_MAX_DATA;
                busDelay(uartT);
                busState = -SDB_MAX_DATA;
            } else {
                busState = SDB_MAX_DATA;
                busDelay(uartT);
                busState = -SDB_MAX_DATA;
            }
        }
        // бит честности
        if (uartParityBit != 0) {
            if ((pBit & 0x0001) == 0) {
                busState = -SDB_MAX_DATA;
                busDelay(uartT);
                busState = -SDB_MAX_DATA;
            } else {
                busState = SDB_MAX_DATA;
                busDelay(uartT);
                busState = -SDB_MAX_DATA;
            }
        }
        // стоп бит
        busState = -SDB_MAX_DATA;
        for (register unsigned char i = 0; i < uartStopBit; i++)
            busDelay(uartT);
        busState = -SDB_MAX_DATA;
    }

    // функция устанавливает скорость UART
    void sdb::uartSetBaudrate(unsigned long baudrate) {
        if (baudrate > SDB_UART_BAUDRATE_MAX)
            baudrate = SDB_UART_BAUDRATE_MAX;
        uartBaudrate = baudrate;
        uartT = 1000000 / baudrate;
    }

    void sdb::uartSetBit(unsigned char bit) {
        if (bit > 32)
            bit = 32;
        if (bit == 0)
            bit = 1;
        if (bit < 8)
            bit = 8;
        uartBit = bit;
    }

    void sdb::uartSetStopBit(unsigned char bit) {
        if (bit == 0)
            bit = 1;
        uartStopBit = bit;
    }

    void sdb::uartSetParityBit(unsigned char state) {
        if (state > 1)
            state = 1;
        uartParityBit = state;
    }

    //функция определяет есть ли устройство на шине
    void sdb::oneWireReset(void) {
        busState = SDB_MAX_DATA;
        busDelay(100);
        busState = 0;//логический "0"
        busDelay(485);//ждем минимум 480мкс
        busState = SDB_MAX_DATA;
        busDelay(65);//ждем минимум 60мкс и смотрим что на шине
        busState = 0;//логический "0"
        busDelay(400);
        busState = SDB_MAX_DATA;
        busDelay(100);
    }

    // функция останавливает шину 1-wire
    void sdb::oneWireStop(void) {
        if (isCreateWavFileFlag == 1) {
            if (posBufferOne > 0) {
                if (isWavFileOutput == 1)
                    writeDataBlockWavFile(busDataOne,posBufferOne);
                #if SDB_WITH_OPENAL == 1
                    if (isAudioOutput == 1)
                        setBufferOpenAl(busDataOne,posBufferOne);
                #endif
            } else
            if (posBufferTwo > 0) {
                if (isWavFileOutput == 1)
                    writeDataBlockWavFile(busDataTwo,posBufferTwo);
                #if SDB_WITH_OPENAL == 1
                    if (isAudioOutput == 1)
                        setBufferOpenAl(busDataTwo,posBufferTwo);
                #endif
            }
            #if SDB_WITH_OPENAL == 1
                while (1) {
                    // Ждем, когда звук проиграет до конца
                    if (updateOpenAl() == 0)
                        break;
                }
                closeOpenAlSound();
                destroyOpenAL();
            #endif
            if (isWavFileOutput == 1)
                closeWavFile();
            isCreateWavFileFlag = 0;
        }
    }

    void sdb::uartStop(void) {
        if (isCreateWavFileFlag == 1) {
            if (posBufferOne > 0) {
                if (isWavFileOutput == 1)
                    writeDataBlockWavFile(busDataOne,posBufferOne);
                #if SDB_WITH_OPENAL == 1
                    if (isAudioOutput == 1)
                        setBufferOpenAl(busDataOne,posBufferOne);
                #endif
            } else
            if (posBufferTwo > 0) {
                if (isWavFileOutput == 1)
                    writeDataBlockWavFile(busDataTwo,posBufferTwo);
                #if SDB_WITH_OPENAL == 1
                    if (isAudioOutput == 1)
                        setBufferOpenAl(busDataTwo,posBufferTwo);
                #endif
            }
            #if SDB_WITH_OPENAL == 1
                if (isAudioOutput == 1) {
                    while (1) {
                        // Ждем, когда звук проиграет до конца
                        if (updateOpenAl() == 0)
                            break;
                    }
                    closeOpenAlSound();
                    destroyOpenAL();
                }
            #endif
            if (isWavFileOutput == 1)
                closeWavFile();
            isCreateWavFileFlag = 0;
        }
    }

    void sdb::setWavFileName(char* filename) {
        strcat(wavFileName,filename);
    }

    void sdb::playAudioOn(void) {
        if (isCreateWavFileFlag == 0)
            isAudioOutput = 1;
    }

    void sdb::playAudioOff(void) {
        if (isCreateWavFileFlag == 0)
            isAudioOutput = 0;
    }

    void sdb::recordOn(void) {
        if (isCreateWavFileFlag == 0)
            isWavFileOutput = 1;
    }

    void sdb::recordOff(void) {
        if (isCreateWavFileFlag == 0) {
            if (isAudioOutput == 1)
                isWavFileOutput = 0;
            else
                isWavFileOutput = 1;
        }
    }


Файл main.h
#ifndef MAIN_H_INCLUDED
#define MAIN_H_INCLUDED

#define LINUX 0x00
#define WINDOWS 0x01

#define RU 0x00
#define EN 0x01

/// Тип операционой системы
#define TYPE_OS WINDOWS

/// Язык программы
#define LANGUAGE_PROGRAM RU

#define UART_BUS 0x01
#define ONE_WIRE_BUS 0x02

#include <iostream>
#include "SoundsDigitalBus.h"
#include "stdlib.h"
#include <stdio.h>

#endif // MAIN_H_INCLUDED


Файл main.cpp
#include "main.h"

sdb soundsDigitalBus;

int main() {
    static FILE *fp = NULL; // файл с данными
    char strData[512]; // буфер для строк
    char strChar = 0; // символ
    unsigned char busType; // тип цифровой шины
    int strPos = 0; // позиция в строке
    int uartBaudrate = 0; // скорость UART
    int uartBit = 8;
    int uartStopBit = 0;
    //int uartParityBit = 0;

    #if TYPE_OS==WINDOWS and LANGUAGE_PROGRAM==RU
    setlocale(LC_ALL, "Russian");
    printf("Введите скорость UART в бод, или укажите 0, если хотите 1-wire.\n");
    #else
    printf("Enter the UART baud rate, or specify 0 if you want 1-wire.\n");
    #endif
    printf("UART Baudrate: ");
    memset(strData,0,512);
    while(1) {
        strChar = getchar();
        if ((strChar >= '0') && (strChar <= '9')) {
            strData[strPos++] = strChar;
        } else
            break;
    }
    uartBaudrate = atoi(strData);
    if (uartBaudrate == 0) {
        busType = ONE_WIRE_BUS;
    } else {
        busType = UART_BUS;
        soundsDigitalBus.uartSetBaudrate(uartBaudrate);
    }
    printf("\n");
    if (busType == UART_BUS) {
        #if TYPE_OS==WINDOWS and LANGUAGE_PROGRAM==RU
        printf("Введите количество бит UART\n");
        #else
        printf("Enter the number of bits UART.\n");
        #endif
        printf("UART bit: ");
        memset(strData,0,512);
        while(1) {
            strChar = getchar();
            if ((strChar >= '0') && (strChar <= '9')) {
                strData[strPos++] = strChar;
            } else
                break;
        }
        uartBit = atoi(strData);
        soundsDigitalBus.uartSetBit(uartBit);
        printf("\n");
        #if TYPE_OS==WINDOWS and LANGUAGE_PROGRAM==RU
        printf("Введите количество стоп бит UART\n");
        #else
        printf("Enter the number of stop bits UART.\n");
        #endif
        printf("UART stop bit: ");
        memset(strData,0,512);
        while(1) {
            strChar = getchar();
            if ((strChar >= '0') && (strChar <= '9')) {
                strData[strPos++] = strChar;
            } else
                break;
        }
        uartStopBit = atoi(strData);
        soundsDigitalBus.uartSetStopBit(uartStopBit);
        printf("\n");
        #if TYPE_OS==WINDOWS and LANGUAGE_PROGRAM==RU
        printf("Использовать бит четности в UART? (Y/n)\n");
        #else
        printf("Use the parity bit in the UART? (Y/n)\n");
        #endif
        strChar = getchar();
        if ((strChar == 'n')
            || (strChar == 'N')
            || (strChar == 'т')
            || (strChar == 'Т')) {
            soundsDigitalBus.uartSetParityBit(0);
            printf("not used\n");
        } else {
            soundsDigitalBus.uartSetParityBit(1);
            printf("Yes, use\n");
        }
        getchar();
        printf("\n");
    }


    FILE_M:
    printf("\n");
    #if TYPE_OS==WINDOWS
    printf("Укажите файл для преобразования его в запись цифровой шины.\n");
    printf("Например: D: \\ Games \\ SR2 \\ Rangers.txt\n");
    printf("Файл: ");
    #else
    printf("Specify the file to convert it to record digital bus.\n");
    printf("For example: D: \\ Games \\ SR2 \\ Rangers.txt\n");
    printf("File: ");
    #endif
    memset(strData,0,512);
    strPos = 0;
    while(1) {
        strChar = getchar();
        if (strChar != '\n') {
            strData[strPos++] = strChar;
        } else
            break;
    }
    fp = fopen(strData,"rb");
    if (fp == NULL) {
        printf("\n");
        #if TYPE_OS==WINDOWS
        printf("Ошибка! Файл %s не найден!\n",strData);
        printf("Поробуйте правильно указать путь до файла.\n");
        printf("...\n");
        #else
        printf("Error! File %s not found!\n",strData);
        printf("Try to correctly specify the path to the file.\n");
        printf("...\n");
        #endif
        getchar();
        goto FILE_M;
    }
    //soundsDigitalBus.setWavFileName(strData);
    printf("\n");
#if SDB_WITH_OPENAL == 1
    #if TYPE_OS==WINDOWS
    printf("Воспроизводить аудио во время работы цифровой шины? (Y/n)\n");
    #else
    printf("Play audio while working digital bus? (Y/n)\n");
    #endif
    strChar = getchar();
    if ((strChar == 'n')
        || (strChar == 'N')
        || (strChar == 'т')
        || (strChar == 'Т')) {
        soundsDigitalBus.playAudioOff();
        printf("not used\n");
    } else {
        soundsDigitalBus.playAudioOn();
        printf("Yes, use\n");
    }
    getchar();
    printf("\n");
    #if TYPE_OS==WINDOWS
    printf("Записывать аудио во время работы цифровой шины? (Y/n)\n");
    #else
    printf("Record audio while working digital bus? (Y/n)\n");
    #endif
    strChar = getchar();
    if ((strChar == 'n')
        || (strChar == 'N')
        || (strChar == 'т')
        || (strChar == 'Т')) {
        soundsDigitalBus.recordOff();
        printf("not used\n");
    } else {
        soundsDigitalBus.recordOn();
        printf("Yes, use\n");
    }
    getchar();
#else
    soundsDigitalBus.recordOn();
#endif
    printf("\n");
    #if TYPE_OS==WINDOWS
    printf("Преобразование начато.\n");
    #else
    printf("The transformation started.\n");
    #endif
    unsigned char uartData[8];
    if (busType == ONE_WIRE_BUS) {
        soundsDigitalBus.oneWireReset();
    }
    while(1) {
        if (fread(uartData,sizeof(unsigned char),1,fp) > 0) {
            if (busType == UART_BUS) {
                if (uartBit == 8) {
                    soundsDigitalBus.uartSendByte(uartData[0]);
                } else {
                    soundsDigitalBus.uartSend(uartData[0]);
                }
            } else
            if (busType == ONE_WIRE_BUS) {
                soundsDigitalBus.oneWireSendByte(uartData[0]);
            }
        } else
            break;
    }
    fclose(fp);
    if (busType == ONE_WIRE_BUS) {
        soundsDigitalBus.oneWireStop();
    } else
    if (busType == UART_BUS) {
        soundsDigitalBus.uartStop();
    }
    #if TYPE_OS==WINDOWS
    printf("Преобразование завершено.\n");
    #else
    printf("Conversion completed.\n");
    #endif
    return 0;
    //soundsDigitalBus.oneWireReset();
    soundsDigitalBus.uartSetBaudrate(1200);
    for (int i = 0; i < 256; i ++) {
        for (int len = 0; len < 8; len++) {
            soundsDigitalBus.uartSendByte(i);
        }
        printf("%d\n",i);
    }
    soundsDigitalBus.oneWireStop();
    return 0;
}


P.S. Заметил ошибку, что в исходном коде стартовый бит равен логической 1, а не 0, а стоповый равен 0, а не 1. Кому необходимо принципиальное соотвествение звукового сигнала реальности, может исправить ошибку сам.

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


  1. mokhin-denis
    29.11.2017 07:29

    Так загрузка игр с магнитофона звучала на моём ZX Spectrum 48M


    1. Akon32
      29.11.2017 10:08

      На кассету данные записывались с фазоинверсной модуляцией, и на скорости около 1500bps.
      Кажется, отличия на слух есть, но не уверен, что я различил бы.


    1. seri0shka
      29.11.2017 19:49

      А я то думаю: где я это слышал? Радио 86РК


    1. pulsatrix
      29.11.2017 22:32

      А как пел Dial-up когда-то.
      А еще я серву к ардуине, запитаной от usb, подключил. Так серва начала мне бит отстукивать. Я сначала подумал она мне морзянкой что-то сказать хочет. Оказалось питания не хватало и ардуина перезагружалась периодически.


  1. PKav
    29.11.2017 14:37

    Мне очень интересно как звучит сигнал спутников GPS. Пытался поймать его на SDR-брелок, ничего не получилось, хотя 900 и 1800 МГц сотовые сети слышно хорошо. Даже активная антенна не помогает.

    UART не очень хороший «источник звука» — у него есть старт-бит. Лучше послушать SPI. По нему работает, например, карта памяти SD.