Advertisement
Guest User

Untitled

a guest
Aug 21st, 2015
520
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 69.56 KB | None | 0 0
  1. // чем отличаются byteRate blockAlign bitsPerSample ???
  2.  
  3.  
  4. // wav-файл - двоичный, не текстовый
  5.  
  6. /* Ссылки на сайты, которые еще необходимо изучить:
  7. http://microsin.net/programming/PC/wav-format.html
  8. http://www.johnloomis.org/cpe102/asgn/asgn1/riff.html
  9. http://soundfile.sapp.org/doc/WaveFormat/
  10. http://www.johnloomis.org/cpe102/asgn/asgn1/riff.html
  11. http://alexei-s1.narod.ru/WAVE.htm
  12. http://muzlib.com/node/910
  13. http://blog.eqlbin.ru/2011/02/wave-java.html
  14. http://compfaq.ru/Question5f94.html
  15. */
  16. // !!!!!!!!!!!!   http://microsin.net/programming/PC/wav-format.html (распечатать)
  17.  
  18.  
  19. // WAV-файлы могут содержать строки текста, например метки секций, информационные комментарии и т. д. Строки сохраняются таким образом, что первый байт указывает количество байт текста ASCII в строке.
  20. /*
  21. WAV-файл использует стандартную RIFF-структуру, которая группирует содержимое файла из отдельных секций (chunks) - формат выборок аудиоданных, аудиоданные, и т. п. Каждая секция имеет свой отдельный заголовок секции и отдельные данные секции. Заголовок секции указывает на тип секции и количество содержащихся в секции байт. Такой принцип организации позволяет программам анализировать только необходимые секции, пропуская остальные секции, которые не известны или которые не требуют обработки. Некоторые определенные секции могут иметь в своем составе подсекции (sub-chunks). Например, как можно увидеть на диаграмме, описывающий основной формат WAV-файла, секции "fmt " и "data" являются подсекциями секции "RIFF".
  22.  
  23. Chunk ID "RIFF"
  24. Chunk Data Size
  25. RIFF Type ID "WAVE"
  26. Chunk ID "fmt "
  27. Chunk Data Size
  28. Sample Format Info
  29. Chunk ID "data"
  30. Chunk Data Size
  31. Digital Audio Samples
  32. Заголовок секции (Chunk Header)
  33. Данные секции (Chunk Data Bytes)
  34. */
  35. /*
  36. Макрос errno возвращает последний номер ошибки. Этот макрос заменяется на модифицируемое значение типа int, поэтому errno может изменять ход работы программы.
  37. При запуске программы errno инициализируется нулем. Некоторые функции стандартной Си-библиотеки могут изменить его значение на значение отличное от нуля, чтобы сигнализировать о возникновении  ошибок. Вы также можете изменить значение errno или обнулить его, на ваше усмотрение.
  38. В этом же заголовочном файле <cerrno> определены также, по крайней мере, следующие две константы со значениями отличными от нуля:
  39.  
  40. EDOM   
  41. Ошибка области допустимых значений:
  42. Некоторые математические функции работают только с определенными значениями, которые называют своей областью допустимых значений. Например, квадратный корень вычисляется только для неотрицательных чисел, поэтому функция sqrt устанавливает макрос ERRNO в EDOM, если вызывается с отрицательным аргументом.
  43.  
  44. ERANGE 
  45. Ошибка диапазона значений:
  46. Диапазон значений, которые могут быть представлены типами данных, ограничен. Например, математические функции, такие как pow, могут с легкостью вернуть значение, которое не поместится  ни в одном типе данных.  В этих случаях, ERRNO устанавливается в ERANGE.
  47.  
  48. */
  49.  
  50. #include <stdio.h>          // Staхndart input/output работа с файлами и стандартным потоков ввода-вывода + perror and error thread, and function !!!fread()!!!
  51. #include <stdlib.h>         // заголовочный файл стандартной библиотеки языка Си (зачем она здесь) ???  определяют макрос NULL
  52. #include <sys/io.h>         // ioperm, outb, inb
  53. #include <sys/ioctl.h>      // работа с основными параметрами в файлах-устройств (запись-чтение в файлах устройств)
  54. #include <errno.h>          // errno.h — заголовочный файл стандартной библиотеки языка программирования С[1], содержащий объявление макроса для идентификации ошибок через их код.
  55. #include <unistd.h>         // for ioperm      usleep   // In the C and C++ programming languages, unistd.h is the name of the header file that provides access to the POSIX operating system API. POSIX (/ˈpɒzɪks/ poz-iks), an acronym for Portable Operating System Interface,[1] is a family of standards specified by the IEEE Computer Society for maintaining compatibility between operating systems. POSIX defines the application programming interface (API), along with command line shells and utility interfaces, for software compatibility with variants of Unix and other operating systems.
  56. #include <fcntl.h>          // <fcntl.h> описаны запросы и аргументы для системных вызовов fcntl(2) и open(2).    // используется для ф-ии fcntl в данной программе
  57. #include <termios.h>            // Используются ф-ии: TCSETATTR.    The <termios.h> header contains the definitions used by the terminal I/O interfaces (see the XBD specification, General Terminal Interface  for the structures and names defined). Функции termios описывают общий терминальный интерфейс, гарантирующий управление асинхронным коммуникационным портом.
  58. #include <sched.h>              // Linux/include/linux/sched.h  (планировщик) SCHED_FIFO приоритетное планирование; SCHED_OTHER приоритетное планирование с разделением времени; SCHED_RR дополнительная стратегия планирования.    used ioperm
  59. #include "playwav64.h"          // подключаем собственную библиотеку
  60.  
  61. // файл читается буффером в 64 КБ
  62. // ioperm позволяет открыть доступ ко всем портам от 0 до 0x3FF
  63.  
  64. /*
  65. http://audiocoding.ru/formats/wav/                                 // краткое описание wav-формата
  66. http://audiocoding.ru/article/2008/05/22/wav-file-structure.html   // структура wav-файла (статья)
  67.  
  68. Совокупность амплитуды и короткого промежутка времени носит название сэмпл.
  69. Основной концепцией RIFF-формата является chunk, порция данных с заголовком и сигнатурой, указывающей на содержимое chunk’а.
  70.  
  71. Использование формата WAV является общепринятым, благодаря его простоте и простой структуре, которая в большой степени основана на формате файлов RIFF.
  72.  
  73. Битрейт (от англ. bitrate) — количество бит, используемых для хранения одной секунды мультимедийного контента[1][2]. Битрейт принято использовать при измерении эффективной скорости передачи потока данных по каналу, то есть минимального размера канала, который сможет пропустить этот поток без задержек.
  74. Битрейт выражается битами в секунду (бит/c, bps), а также производными величинами с приставками кило- (кбит/с, kbps), мега- (Мбит/с, Mbps) и т. д.
  75.  
  76. Итак, рассмотрим самый обычный WAV файл (Windows PCM). Он представляет собой две, четко делящиеся, области. Одна из них – заголовок файла, другая – область данных. В заголовке файла хранится информация о:
  77.  
  78. Размере файла.
  79. Количестве каналов.
  80. Частоте дискретизации.
  81. Количестве бит в сэмпле (эту величину еще называют глубиной звучания).
  82.  
  83.  
  84.     Wav-файл - это последовательность значений, получаемых с заданной частотой (частотой дискретизации - это означает как часто мы берем значение) с АЦП (аналого-цифрового преобразователя) и записанных в файл как есть, для каждого из канал.
  85.     С точки зрения звука - это громкость в данный момент времени или с точки зрения динамика - положение пембраны относительно точки покоя.
  86.     Максимальное значение, полученное с АЦП (аналого-цифрового преобразователя) за раз, задает величину битового потока (bitrate - количество передаваемых бит в секунду)
  87.  
  88. Так выглядит заголовок wav-файла:
  89.    
  90.     Смещение   Байт  Описание
  91. ------------------------------------------------------------------------
  92. 0x00 (00)  4     "RIFF", сигнатура
  93. 0x04 (04)  4     размер фала в байтах минус 8
  94. 0x08 (08)  8     "WAVEfmt "
  95. 0x10 (16)  4     16 для PCM, оставшийся размер заголовка
  96. 0x14 (20)  2     1 для PCM, иначе есть какое-то сжатие
  97. 0x16 (22)  2     число каналов - 1, 2, 3...
  98. 0x18 (24)  4     частота дискретизации
  99. 0x1c (28)  4     байт на одну секунду воспроизведения
  100. 0x20 (32)  2     байт для одного сэпла включая все каналы
  101. 0x22 (34)  2     бит в сэмпле на один канал
  102. 0x24 (36)  4     "data" (id сабчанка)
  103. 0x28 (40)  4     сколько байт данных идет далее (размер сабчанка)
  104. 0x2c (44)  -     данные
  105.    
  106.     Совокупность записей данных обо всех каналах в данный момент времени - это сэмпл.
  107.     Прикрепить статью из Хабра
  108.    
  109.     good playing 3.wav Johan Sebastian Bach on 700000  
  110.  
  111.     http://geektimes.ru/company/audiomania/blog/241620/                 // вводная статья
  112.     http://eax.me/scala-wav/
  113.     http://habrahabr.ru/post/113239/
  114.     http://audiocoding.ru/article/2008/05/22/wav-file-structure.html
  115.     https://www.google.ru/?gws_rd=ssl#newwindow=1&q=%D0%BA%D0%B0%D0%BD%D0%B0%D0%BB+wav+%D1%84%D0%B0%D0%B9%D0%BB%D0%B0
  116.     http://geektimes.ru/company/audiomania/blog/241620/
  117.    
  118.     http://rus-linux.net/MyLDP/BOOKS/lkmpg-1/node18.htm
  119. */
  120.  
  121. void main (int argc, char *argv[]) { // аргументы, передаваемые командной строкой в программу argc - кол-во аргументов >= 1 всегда, argv является указателем на массив указателей на строки   В этом массиве каждый элемент указывает на какой-либо аргумент командной строки.
  122. // argv[0] - pointer on name of exec file: playwav64
  123.     FILE *fwav = NULL;      // Инициализируем указатель fwav на файл
  124. //  printf("%s\n", *(argv+1));  // print on terminal name of wav file          
  125.     wavhead wh;         // Объявляем экземпляр структуры ""Заголовок wav-файла"
  126.     whfmt *whft = NULL;   // какая-то структура             // wave head format
  127.        
  128.     char playmode = 0;      // Режим воспроизведения (standart or with new frequency)   can be 0, 1 (default frequency) or 2 (user frequency)
  129.    
  130.     int status = 0; // Размер данных
  131.    
  132.     char wavdata[0x10000];      // Буфер с данными
  133.     unsigned int *curdata = NULL;   // Указатель на текущие данные из буфера
  134.     unsigned int bufsize = 0;   // Объём данных в буфере
  135.    
  136.     int i = 0, k = 0;   // два индекса
  137.    
  138.     unsigned int cursampleraw = 0;      // Текущее значение из буфера
  139.     unsigned int datamask = 0xFFFFFFFF; // Маска соответствующая размеру данных на значение в канале
  140.     int cursample = 0;                  // смещение для текущего сэмпла, позиция текущего семпла
  141.     short int onechannelinc = 0;        // Приращение позиции данных в буфере = размер сэмпла
  142.     unsigned int samplezero = 0;        // Значение нулевого уровня
  143.    
  144.     short int pause = 0;        // Присваиваем для паузы ноль ???
  145.     int onesecond = ONESECOND;  // Определяем одну секунду (сколько тактов буде совершать процессором/программой за секунду) ????
  146.    
  147.     char kbh_ch = EOF;      // переменной kbh_ch присвоили "конец файла"          keyboard hit character = EOF возможно, такая расшифровка
  148.        
  149.     unsigned char out61 = 0;    // вывод on 61-й порт
  150.     short int permit61 = -1;    // разрешение 61 (-1: наверно, не разрешено)
  151.    
  152.     if (argc < 2) {             // если аргументов меньше, чем 2, то выводим ошибку об остутствии аргументов (user didn't enter name of wav file)
  153.         ShowErr(ENOARG, NULL);  // не хватает аргументов terminal-a (не ввели название wav файла ) // вывести ошибку по коду ENOARG (ошибка номера аргумента = 3) Второй параметр = NULL, что обозначает?
  154.     }
  155.     else {
  156.         if ((playmode = CheckArgs(argc)) >= ARGSETTIME && !(onesecond = atol(argv[2]))) {    // playmode присваиваем результат ф-ии "проверка аргументов" ARGSETTIME = 2       // atol преобразует строку в длинное (long) число в качестве строки выступает множитель времени
  157.         // если кол-во аргументов >= 2 и значение параметра "множителя времени" равно нулю, то переменной "onescond" присваиваем значение по умолчанию.
  158.                 onesecond = ONESECOND;  // переменной "одна секунда" присваиваем значение по умолчанию для одной секунды (из заголовочного файла playwav64.h)
  159.         }
  160.     }
  161.     if (playmode && (status = InitWavFile(&fwav, argv[1], &wh, &whft)) > 0) {  // we have not any errors with Initialization of wav-file and playwode = 1 or 2 (default or user frequency) then...
  162.             // InitWavFile(&fwav, argv[1], &wh, &whft)) > 0: инициализация wav-файла прошла успешно
  163.  
  164.         SetRawKbd(0);           // set keyboard to raw mode   (установить клавиатуру в необработанный режим) ???
  165.        
  166.         //scrsize.ws_col - размеры по горизонтали
  167.         //scrsize.ws_row - размеры по вертикали  
  168.        
  169.         /*
  170.             Файлы устройства представляют физические устройства. Многие физические устройства используются для вывода и для ввода, так должен иметься некоторый механизм для драйверов устройства в ядре, чтобы послать вывод устройству из процесса. Это выполняется, открывая файл устройства для вывода и записывая в него точно так же, как в обычный файл. В следующем примере, это выполнено функцией device_write.
  171.             Этого не всегда достаточно. Вообразите, что Вы имеете последовательный порт, связанный с модемом (даже если Вы имеете внутренний модем, он выглядит с точки зрения CPU как последовательный порт, связанный с модемом, так что Вы не должны слишком сдерживать Ваше воображение). Естественное решение использовать файл устройства, чтобы передавать модему команды модема или данные, которые будут посланы через телефонную линию и читать из модема ответы для команд или данные, полученные через телефонную линию. Однако, это оставляет открытым вопрос о том что делать, когда Вы должны работать с последовательным портом непосредственно, например настроить скорость обмена данными с портом.  
  172.             Ответ в Unix должен использовать специальную функцию, названную ioctl (сокращение от input output control). Каждое устройство может иметь собственные команды ioctl, которые могут читать ioctl (для передачи данных от процесса ядру), записывать ioctl (чтобы возвратить информацию процессу), 5.1, выполнять оба действия или ни одно из них. Функция ioctl вызывается с тремя параметрами: описатель файла соответствующий файлу устройства, ioctl номер, и параметра, который имеет тип long, так что Вы можете использовать приведение, чтобы передать что-нибудь. 5.2
  173.        
  174.             Аргументы: File-descriptor (описатель файла), соответствующий данному файлу устройства, ioctl номер (???), и параметр, который имеет тип long long
  175.        
  176.             (возможно по ioctl мы что-то должны указать в файле заголовка playwav64.h)Ioctl номер кодирует главный номер устройства, тип ioctl команды и тип параметра. Этот ioctl номер обычно создается макрообращением (_IO, _IOR, _IOW или _IOWR: в зависимости от типа) в файле заголовка. Этот файл заголовка должен быть присоединен командой #include программой, которая использует ioctl и модулем (так что они могут генерировать соответствующие ioctl).
  177.         */
  178.          
  179.             // bits per sample sets by pointer whft
  180.             onechannelinc = whft -> numchannels * whft -> bitspersample >> 3;   // один канал инкремент = кол-во каналов * кол-во бит в сампле / 8 ?????   // bitsPerSample Количество бит в сэмпле. Так называемая “глубина” или точность звучания. 8 бит, 16 бит и т.д.
  181.             samplezero = 1 << (whft -> bitspersample-1);            // нулевой сампл равен половине максимально возможной амплитуды (схема на записана листе)
  182.             pause = onesecond / whft -> samplerate;             // пауза будет длиться = 1 сек / Скорость выборок (Sample Rate = по-сути это частота дискретизации)Число выборок аудиосигнала, приходящихся на секунду. На эту величину не влияет количество каналов.
  183.             // pause - это значит, что через каждый отсчет (выборку) будет происходить задержка (пустой пропуск без звука), пример записан на листе
  184.            
  185.             if ((whft -> bitspersample >> 3) < sizeof(cursampleraw)) {          // если кол-во байт на сампл (максимально возможное) < размер текущего необработанного сампла, то...
  186.                 datamask = ((1 << whft -> bitspersample)-1);    // рассчитывваем маску данных        что обозначает datamask?   маска данных = (2^(кол-во бит на сампл))-1
  187.             }
  188.        
  189.             if (playmode <= ARGSETTIME) {                       // если режим воспроизведения < 3
  190.                 if (permit61 = InitIOPorts(0)) ShowErr(EACSDND, argv[0]);   // пытаемся разрешить доступ к 61-му порту, если не вышло, то выводим сообщение об ошибке
  191.             }
  192.             else {
  193.                 permit61 = 0;          // разрешаем 61-й порт
  194.             }
  195.        
  196. //Start main read file     
  197.         while (!feof(fwav) && kbh_ch == EOF && bufsize >= 0 && !permit61) { // до тех пор, пока: 1 - не закончился файл, 2 -  kbh_ch == не конец файла, 3 - размер буффера неотрицателен, размер данных неотрицателен и и есть разрешение на 61-й порт
  198.             bufsize = FileRead(&fwav, wavdata, sizeof(wavdata)-sizeof(cursample));   // присваиваем переменной "размер буффера" значение ф-ии File Read от аргументов: 1 - указатель на wav-файл, 2 - указатель на буфер с данными, 3 - разность между размером буффера и позицией текущего сампла (т.е. сколько еще осталось прочитать)
  199.            
  200.  
  201. //Start main play .wav data  
  202.             // kbh_ch - символ нажатой клавиши (keyboard hit) kbh_ch = getchar() - считать символ с клавиатуры
  203.             // до тех пор, пока мы не пройдем по всему буфферу wav-файла и пока не нажата какая-либо клавиша, мы переходим к следующему самплу и воспроизводим его. А затем выводим задержку.
  204.             for (i = 0; i < bufsize && ((kbh_ch = getchar()) == EOF); i += onechannelinc) {
  205.                 curdata = (void*)(wavdata+i);       // Определили указатель на текущую позицию (это адрес начала буффера с данными + смещание i)
  206.                 cursampleraw = *curdata&datamask;   // Выделили значимое для нас
  207.            
  208. //Play in pc-speaker
  209.                 // OK!
  210.                 if (playmode <= ARGSETTIME) {
  211.                     if (cursampleraw > samplezero) {    // Если разница между "необработанной" частотой (оригинальной) и нулевым самплом, который (скорее всего лежит посередине области значений амплитуды) > 0, то воспроизводим звук, иначе пауза
  212.                         out61 |= 0x2;       // Включить спикер, выставляем первый бит = 1 (в двоичной системе: были все нули, а стало: 00000010 первый бит равен 1)        
  213.                     }                    
  214.                     else {
  215.                         out61 &= 0xFD;  // в данном случае операция логического "и" обнуляет все разряды 61-го порта    // Иначе выключить, выставляем первый бит = 0 (this is useful: FD = 253) (в двоичной системе: 11111101 первый бит равен 1)    
  216.                     }
  217.                     for (k = 0; k < pause; k++) outb(out61, SPKPORT);   // Совмещаем задержку и вывод (spkport - порт спикера) (т.е. выводим паузу)
  218.                 }  
  219.             }
  220.         }
  221.         if (bufsize < 0) ShowErr(EFREAD, argv[1]);      // если размер буффера отрицательный, то выводим ошибку чтения с аргуменом argv[1]??? 
  222.         SetRawKbd(1);                                   // восстановили канонический режим ввода с клавиатуры на терминал и включили эхо (ECHO)
  223.      }
  224.      else ShowErr((char)status, argv[1]); // выводим сообщение об ошибке than we have error with playmode = 0 or error wav-file initialization
  225.      
  226.      if ((playmode <= ARGSETTIME) && permit61 == 0) InitIOPorts(1);             // InitIOPorts(1); аргумент: 1 - обозначает, что мы собираемся восстановить старое значение 61-го порта
  227.      
  228.     if (fwav) fclose(fwav);             // fwav - указатель на wav-файл, если он не нулевой, то мы его закрываем (т.е. если был открыт, то закрываем)
  229. }
  230.  
  231. // OK!
  232. void ShowErr(char err, char *merr) {                // ф-ия вывода ошибки (символ ошибки, указатель на что-то)    // merr: message error
  233.     switch (err) {                           // в зависимости от номера ошибки...
  234.         case EFOPEN:                        // случай ошибки открытия файла
  235.         case EFREAD:                        // случай ошибки чтения файла
  236.         case EACSDND:                       // случай ошибки...
  237.             /* Print error message
  238. Interprets the value of errno as an error message, and prints it to stderr (the standard error output stream, usually the console), optionally preceding it with the custom message specified in str.
  239. */
  240.             perror(merr);               // prints it to stderr, the standard error output stream, usually the console (стандартный поток ошибки)
  241.             break;
  242.         case EHEAD:                 // error head - head error "Unavailable .WAV"
  243.             puts(MEHEAD);           // вывести сообщение об ошибке "недоступен wav-файл"
  244.             break;
  245.         case ENOARG:                // error no arg (ошибка отсутствия аргумента)
  246.             puts(MENOARG);         // вывести инструкции о том, как воспроизвести wav-файл (message no argument)
  247.             break;
  248.         case ENUDATA:               // error number data - ошибка номера данных, возможно ???
  249.             puts(MENUDATA);         //  вывести "No data in file"
  250.             break;
  251.         default: puts(MEGEN);                  // по умолчанию выводим (in other case) "Error!"  
  252.     }
  253.     return;
  254. }
  255.  
  256. /*
  257.         Медиаконтейнер, мультимедиаконтейнер (англ. Media container) — формат файла или потоковый формат (поток необязательно должен быть сохранён в виде файла), чьи спецификации определяют только способ сохранения данных (а не алгоритм кодирования) в пределах одного файла. Медиаконтейнер определяет, сколько метаданных фактически может быть сохранено, вместе с тем он не определяет никакую кодификацию самих данных. Медиаконтейнер фактически является метаформатом, так как он хранит данные и информацию о том, как данные будут сохраняться непосредственно внутри файла. Как следствие из этого, программа, которая способна корректно идентифицировать и открыть файл (прочитать поток), записанный в каком-либо формате, впоследствии может быть не способна декодировать фактические данные, записанные внутри медиаконтейнера, так как или мет
  258.         RIFF (англ. Resource Interchange File Format) — один из форматов файлов-контейнеров для хранения потоковых мультимедиа-данных (видео, аудио, возможно текст). Наиболее известными форматами, использующими RIFF в качестве контейнера, являются: AVI (видео), WAV (аудио), RMI (MIDI-треки).
  259. */
  260.  
  261. //OK!
  262. int FileRead(FILE **fwav, char *wavdata, int readsize) {                    // Читаем файл... аргументы: 1-указатель на файл, 2-указатель на буффер с данными, 3-сколько еще осталось прочитать данных
  263.     int dataread = 0;    // инициализация переменной dataread (чтение данных ?? зачем нужна эта переменная?)
  264.     if (!feof(*fwav)) {    // пока не конец файла
  265.         dataread = fread(wavdata, 1, readsize, *fwav);   // присваиваем значение-результат ф-ии file read. Аргументы: 1-указатель на блок памяти, в который будем записывать данные из буффера (4-го аргумента), 2-размер в байтах одного элемента буффера, 3-кол-во элементов, размера (1), которые нам необходимо считать с буффера *fwav, 4- *fwav - сам буффер, с которого будем считывать данные)
  266.         if (ferror(*fwav)) dataread = -1;   // если произошла ошибка при чтении из буффера *fwav (аргументом проверки является указатель wav-file), то dataread = -1
  267.     }
  268.     return dataread;        // возвращаем значение переменной dataread
  269. }
  270.  
  271. /*
  272.     Основные функции (<termios.h>) работы с режимами терминала:
  273.     int tcgetattr( int fd, struct termios *termios );
  274.     int tcsetattr( int fd, int optional_actions, const struct termios *termios );
  275.     - где optional_actions указывает как поступать с вводом и выводом, уже поставленным в очередь.
  276. */
  277.  
  278. // Установить "Сырой" терминальный ввод/вывод
  279. void SetRawKbd(char restore) {                          // функция "установить необработанный keyboard" ??  Аргумент: восстанавливать (1) или не восстанавливать (0)
  280.    
  281.     /*
  282.             Терминал имеет две очереди ввода и одна - вывода. Символы, вводимые с клавиатуры термина­ла, помещаются в "сырую" очередь ввода. Кроме того, если требуется эхо (вывод печатаемых символов на экран), копии этих символов добавляются в очередь вывода.
  283.             Если разрешен канонический режим обработки ввода (обычно это делается по умолчанию), сим­волы из "сырой" очереди подвергаются предобработке при копировании в каноническую оче­редь ввода. Копирование происходит по строкам, когда поступает символ перевода строки (NL). Пользовательская программа читает строки ввода из канонической очереди.
  284.     */
  285.    
  286.     /*
  287.             Использование termios(3C)
  288.             Существует более пятидесяти различных параметров и флагов, управляющих терминальным ин­терфейсом. Эти параметры можно изменять через ioctl termio(7I), функции termios(3C) и ко­манду stty(1). Ниже приводятся некоторые из основных характеристик терминала
  289.     */
  290.    
  291.     // Объявление переменных для хранения управляющих структур
  292.     static struct termios kbh_oldt;                     // объявляем структуру типа termios
  293.     static struct termios kbh_newt;                     // еще одна структура типа termios "keyboard новый t" ???
  294.     static int kbh_oldf = 0;                            // "keyboard старый f"
  295.     // what function of static ???????????????
  296.     /*
  297.             int
  298.             tcgetattr(int fd, struct termios *t);
  299.  
  300.             int
  301.             tcsetattr(int fd, int action, const struct  termios *t);
  302.            
  303.             The cfmakeraw(), cfmakesane(), tcgetattr()  and tcsetattr() functions are provided for getting and setting the termios structure.
  304.            
  305.              Структура termios
  306.  
  307.                 Структура termios используется для изменения характеристик
  308.                 терминального устройства (скорее всего это клавиатура). Структура одна и та же для всех терминальных
  309.                 устройств, независимо от изготовителя аппаратуры. Это предоставляет
  310.                 единообразный способ изменения характеристик и поведения терминального
  311.                 устройства. В частности, эта структура используется модулем STREAMS
  312.                 для изменения поведения аппаратного и программного интерфейса
  313.                 ввода/вывода.
  314.                 Ниже перечислены поля структуры termios:
  315.                 c_iflag   флаги, управляющие предобработкой ввода с терминала.
  316.  
  317.                 c_oflag   флаги,  управляющие   системной  постобработкой   вывода  на
  318.                           терминал.
  319.  
  320.                 c_cflag   флаги, описывающие  аппаратные характеристики  терминального
  321.                           интерфейса.
  322.  
  323.                 c_lflag   флаги, управляющие разбиением потока на строки.
  324.  
  325.                 c_cc[]    массив специальных управляющих символов.
  326.  
  327.                 Эти поля будут подробнее обсуждаться далее в этом разделе.
  328.                 Ссылка: /usr/include/sys/termios.h
  329.                                           СТРУКТУРА termios
  330.                 #define      NCCS  19
  331.                 struct       termios {
  332.                    tcflag_t    c_iflag;  /* input modes
  333.                    tcflag_t    c_oflag;  /* output modes
  334.                    tcflag_t    c_cflag;  /* control modes
  335.                    tcflag_t    c_lflag;  /* local modes
  336.                    cc_t     c_cc[NCCS];  /* control chars
  337.                 };
  338.                            
  339.            
  340.     */
  341.    
  342.     /*
  343.         The termios general terminal interface provides an interface to asynchronous communications devices. The NuTCRACKER Platform supports this interface for serial communication ports. This interface is also supported on the console window although the hardware-specific parts obviously do not apply.
  344.     */
  345.    
  346.    
  347.     /*
  348.         stdin определен с типом файлового потока FILE*, относящимся к стандартному высокоуровневому вводу-выводу библиотеки C. Заголовок <stdio.h>.
  349.         Функции: fread, fwrite, fclose, fflush и т.д.
  350.  
  351.         STDIN_FILENO это файловый дескриптор (файл, сокет, пайп и т.д.; тип int). Используется не буферизованный ввод/вывод, а прямой вызов системных функций. Заголовок <unistd.h>.
  352.         Функции: read, write, select и т.д.
  353.         Осуществляется последовательный доступ к потокам stdin/out, а так же STDIN_FILENO
  354.     */
  355.    
  356.    
  357.     if (restore) {                                          // если необходимо восстановить: restore = 1
  358.         tcsetattr(STDIN_FILENO, TCSANOW, &kbh_oldt);       
  359.         // Восстановление канонического режима ввода (каким образом)
  360.        
  361.         // kbh_old = 0
  362.         /*
  363.             tcsetattr() устанавливает настройки терминального устройства, такого как консоль, ком-порт и т.д. fcntl() это функция позволяющая различные низкоуровневые манипуляции над файловыми дескрипторами.
  364.         */
  365.        
  366.         /* Второй аргумент может принимать следующие значения:
  367.             #define TCSANOW         0     - нас интересует в этом проекте только это значение
  368.             #define TCSADRAIN       1
  369.             #define TCSAFLUSH       2
  370.            
  371.             TCSANOW - делать изменения немедленно; TCSADRAIN - делать изменения после ожидания, пока весь поставленный в очередь вывод не выведен (обычно используется при изменении параметров, которые воздействуют на вывод); TCSAFLUSH - подобен TCSADRAIN, но отбрасывает любой поставленный в очередь ввод.
  372.         */
  373.        
  374.         /*
  375.                     tcgetpgrp, tcsetpgrp - получение и определение основной группы процессов терминала  
  376.                
  377.            
  378.                     ОПИСАНИЕ
  379.  
  380.                     Функция tcgetpgrp() возвращает идентификатор группы процессов для основной группы процессов терминала, ассоциированного с fd, который должен быть управляющим терминалом для вызывающего процесса.
  381.                     Функция tcsetpgrp() делает группу процессов с идентификатором pgrp основной группой процессов на терминале, ассоциированном с fd, который должен быть управляющим терминалом для вызывающего процесса, и все еще быть ассоциированным с его сессией. Более того, pgrp должно быть (непустой) группой процессов, принадлежащих к той же сессии, что и вызывающий процесс.
  382.  
  383.                     Если tcsetpgrp() вызывается членом группы фоновых процессов в этой сессии, и вызывающий процесс не блокирует или игнорирует SIGTTOU, то сигнал SIGTTOU отправляется всем членам этой группы фоновых процессов.  
  384.  
  385.                     ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ
  386.  
  387.                     Когда fd ссылается на управляющий терминал вызывающего процесса, функция tcgetpgrp() будет возвращать идентификатор основной группы процессов этого терминала, если такой существует, или некоторое значение больше 1, не являющееся в данный момент идентификатором группы процессов, в другом случае. Когда fd не ссылается на управляющий терминал вызывающего процесса, возвращается -1, а переменная errno устанавливается соответственно ошибке.
  388.                     При нормальном завершении работы tcsetpgrp() возвращает 0. Иначе возвращается -1, а переменная errno устанавливается соответственно ошибке.  
  389.  
  390.                     НАЙДЕННЫЕ ОШИБКИ
  391.  
  392.                     EBADF
  393.                     fd не является корректным описателем файла.
  394.                     EINVAL
  395.                     pgrp имеет неподдерживаемое значение.
  396.                     EPERM
  397.                     pgrp имеет поддерживаемое значение, но не является идентификатором группы процессов для процесса в той же сессии, что и вызывающий процесс.
  398.                     ENOTTY
  399.                     Вызывающий процесс не имеет управляющего терминала, или он имеется, но не описан в fd, или, для tcsetpgrp(), этот управляющий терминал больше не ассоциирован с сессией вызывающего процесса.
  400.                      
  401.                     ЗАМЕЧАНИЯ
  402.  
  403.                     Эти функции реализованы через вызовы ioctl TIOCGPGRP и TIOCSPGRP.  
  404.         */
  405.        
  406.         fcntl(STDIN_FILENO, F_SETFL, kbh_oldf);             // Системный вызов fcntl(3)
  407.         // int fcntl(int fd, int cmd, struct flock *lock);
  408.         // Аргументы: 1-файловый дескриптор потока ввода, 2-, 3-
  409.         /*
  410.                 F_SETFL (int)
  411.                 Set the file status flags to the value specified by arg. File access mode (O_RDONLY, O_WRONLY, O_RDWR) and file creation flags (i.e., O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) in arg are ignored. On Linux this command can change only the O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags.
  412.            
  413.                 int fcntl(int fd, int cmd, struct flock * lock);
  414.                 fcntl выполняет различные операции над файловым описателем fd. Рассматриваемая операция определяется значением cmd:
  415.                
  416.                 F_GETFL Прочитать флаги описателя (возвращаются все флаги, установленные при помощи open(2)).
  417.  
  418.                 F_SETFL Установить значение флагов описателя, заданное в arg. Можно установить только флаги O_APPEND, O_NONBLOCK и O_ASYNC (прочие флаги не будут затронуты).
  419.         */
  420.        
  421.         /*Третий аргумент lock является указателем на структуру, которая имеет по крайней мере следующие поля (в произвольном порядке).
  422.  
  423.         struct flock {
  424.             ...
  425.             short l_type;    /* Тип блокировки: F_RDLCK,
  426.                         F_WRLCK, F_UNLCK */
  427.             /* short l_whence;  /* Как интерпретировать l_start:
  428.                         SEEK_SET, SEEK_CUR, SEEK_END */
  429.             /* off_t l_start;   /* Начальное смещение для блокировки */
  430.             /* off_t l_len;     /* Количество байт для блокировки */
  431.             /* pid_t l_pid;     /* PID процесса блокирующего нашу блокировку
  432.                         (F_GETLK only) */
  433.             /* ...
  434.         };
  435.        
  436.        
  437.             Поля l_whence, l_start и l_len этой структуры задают диапазон байт, который мы хотим заблокировать. l_start - это начальное смещение для блокировки, которое интерпретируется как: начало файла (если значение l_whence установлено в SEEK_SET); текущая позиция в файле (если значение l_whence установлено в SEEK_CUR); или конец файла (если значение l_whence установлено в SEEK_END). В последних двух случаях, l_start может иметь отрицательное значение, предоставляя смещение, которого не может быть перед началом файла. l_len -это неотрицательное целое число (но см. ЗАМЕЧАНИЯ ниже), которое задаёт количество байт, которые будут заблокированы. Байты следующие после конца файла могут быть заблокированы, но это нельзя сделать для байтов, которые находятся перед началом файла. Значение 0 для l_len имеет специальное назначение: блокировка всех байтов, начиная от позиции, заданной l_whence и l_start до конца файла, не зависимо от того, насколько велик файл. Поле l_type может быть использовано для указания типа блокировки: чтение (F_RDLCK) или запись (F_WDLCK). Блокировку на чтение (разделяемая блокировка) на область файла может удерживать любое количество процессов, но только один процесс может удерживать блокировку на запись (эксклюзивная блокировка). Любая эксклюзивная блокировка исключает все другие блокировки, как разделяемые так и эксклюзивные. Один процесс может удерживать только один тип блокировки области файла; если происходит новая блокировка на уже заблокированную область, то существующая блокировка преобразуется в новый тип блокировки. (Такие преобразования могут привести к разбиению, уменьшению или срастанию с существующей блокировкой, если диапазон байт, заданный для новой блокировки неточно совпадает с диапазоном существующей блокировки.)
  438.         */
  439.        
  440.         // fcntl - манипуляции с файловым дескриптором
  441.         // F_GETFL (void) Read the file descriptor flags; arg is ignored.               GET FLAGS F_GETFL    Читает флаги файлового дескриптора.
  442.         // F_SETFL (int) Set the file descriptor flags to the value specified by arg.   SET FLAGS
  443.        
  444.         /*
  445.             F_SETFL
  446.             Устанавливает часть флагов, относящихся к состоянию файла, согласно значению, указанному в аргументе arg. Оставшиеся биты (режим доступа, флаги создания файла) в значении arg игнорируются. В Linux данная команда может изменять только флаги O_APPEND, O_NONBLOCK, O_ASYNC и O_DIRECT.
  447.         */ 
  448.     }
  449.     else {
  450.         tcgetattr(STDIN_FILENO, &kbh_oldt);     // Два аргумента: 1-STDIN_FILENO - файловый дескриптор потока ввода, 2-указатель на структуру termios.
  451.         kbh_newt = kbh_oldt;
  452.        
  453.         /*
  454.                 local modes
  455.  
  456.                 ICANON - Perhaps the most important bit in c_lflag is the ICANON bit. Enabling it enables "canonical" mode – also known as "line editing" mode. When ICANON is set, the terminal buffers a line at a time, and enables line editing. Without ICANON, input is made available to programs immediately (this is also known as "cbreak" mode).
  457.                 ECHO in c_lflag controls whether input is immediately re-echoed as output. It is independent of ICANON, although they are often turned on and off together. When passwd prompts for your password, your terminal is in canonical mode, but ECHO is disabled.
  458.                 ISIG in c_lflag controls whether ^C and ^Z (and friends) generate signals or not. When unset, they are passed directly through as characters, without generating signals to the application.
  459.         */
  460.        
  461.         kbh_newt.c_lflag &= ~(ICANON | ECHO);   /* local modes */   // точно выключаем канонический режим + выключаем эхо
  462.        
  463.        
  464.        
  465.         /*
  466.                     Эхо
  467.                     Терминалы обычно работают с UNIX-системами в полнодуплексном режиме. Это означает, что данные передаются в обоих направлениях одновременно и что компьютер обеспечивает эхо (отображение на экране или на печати) получаемых символов. Эхо выключается очисткой флага ECHO в c_lflag.
  468.        
  469.                     Обычно символы при вводе накапливаются, пока не соберется полная строка, завершенная NL. Только после этого удовлетворяется запрос read(2), даже если он требовал только один символ. Значение, возвращаемое вызовом read(2), равно количеству прочитанных в действительности символов. Во многих прикладных программах, таких как редакторы форм или полноэкранные текстовые редакторы, строки ввода не имеют смысла. В таких программах необходимо читать символы по мере их ввода. При очистке флага ICANON в c_lflag вводимые символы не группи­руются в строки, и read(2) читает их по мере поступления. Этот режим известен также как ре­жим неканонического ввода. Вместо этого удовлетворение запроса read(2) определяется пара­метрами MIN (минимальное количество нажатых клавиш) и TIME (промежуток времени
  470.                     между введенными символами). Если ICANON установлен, то режим ввода называется канони­ческим.
  471.         */
  472.        
  473.         /*
  474.             "Сырой" терминальный ввод/вывод
  475.             Терминальные порты могут использоваться для подключения не только терминалов, но и других устройств, например, модемов, мышей или различной контрольно-измерительной аппаратуры. При работе с такими устройствами, прикладные программы и драйверы терминальных устройств могут быть вынуждены передавать и принимать произвольные восьмибитные данные. Это могут быть сообщения мыши о передвижении и нажатии кнопок, данные протокола PPP или поступающие с измерительного устройства данные. Для разрешения этого необходимо устано­вить восьмибитные данные, неканонический ввод, запретить все отображения символов и управ­ление потоком, устранить специальные значения всех управляющих символов и выключить эхо. В конце раздела приводится пример использования "сырого" терминального ввода/вывода.
  476.         */
  477.        
  478.        
  479.         // ICANON Enable canonical mode (described below).
  480.         // ECHO   Echo input characters.
  481.        
  482.         // c_lflag   флаги, управляющие разбиением потока на строки.
  483.         tcsetattr(STDIN_FILENO, TCSANOW, &kbh_newt);
  484.        
  485.         // /* arg == 0 restores default behaviour. */ возможно????  Третий аргумент равен 0, возможно обнуляет все флаги.
  486.         // что обозначает 3-й аргумент?
  487.         kbh_oldf = fcntl(STDIN_FILENO, F_GETFL);            // Теоретически, считываем значения флагов и записываем их в int.
  488.         // kbh_oldf = fcntl(STDIN_FILENO, F_GETFL);  для файлового дескриптора включается режим неблокированного ввода
  489.         // printf("%d\n", kbh_oldf);            // Теоретически не должно быть равно нулю. Выводит 2. Что обозначает 2?   000000010 в двоичной, что обозначает ???
  490.         // возвращаем какое-то целочисленное значение - значение флагов файлового дескриптора
  491.         /*
  492.             fcntl - возвращаемое значение
  493.             При успешном завершении работы возвращаемое значение зависит от того, как действуют следующие установки:
  494.             F_GETFL (значение флагов);
  495.         */
  496.        
  497.         /*
  498.             The third argument lock is a pointer to a structure that has at least the following fields (in unspecified order).
  499.                 struct flock {
  500.                             ...
  501.                 short l_type;     Type of lock: F_RDLCK,
  502.                                     F_WRLCK, F_UNLCK
  503.                 short l_whence;  /* How to interpret l_start:
  504.                                     SEEK_SET, SEEK_CUR, SEEK_END
  505.                 off_t l_start;   /* Starting offset for lock
  506.                 off_t l_len;     /* Number of bytes to lock
  507.                 pid_t l_pid;     /* PID of process blocking our lock
  508.                                     (F_GETLK only)
  509.                 };
  510.         */
  511.        
  512.         fcntl(STDIN_FILENO, F_SETFL, kbh_oldf | O_NONBLOCK);            // kbh_oldf | O_NONBLOCK = 100000000010, активны: 0-ой и 10-й биты
  513.         // неблокирующий режим
  514.        
  515.        
  516.        
  517.         // В настройках терминала отключается канонический режим (редактирование командной строки и т.д.) и автоматический вывод вводимых символов на экран.
  518.        
  519.        
  520.         /*
  521.                     Используемый "по умолчанию" в ОС UNIX режим ввода данных с клавиатуры терминала называется каноническим. Он подразумевает присутствие между клавиатурой терминала и прикладной программой очереди вводимых символов. Символы нажатых клавиш накапливаются в этой очереди до тех пор, пока пользователь не нажмет клавишу "Ввод" (Enter), и только в этот момент осуществляется передача строки накопленных символов в прикладную программу. Выданный в прикладной программе запрос на чтение с клавиатуры (посредством системного вызова read) приостанавливает выполнение программы до момента нажатия клавиши "Ввод".
  522.                     Для некоторых прикладных программ (редакторов экранных форм, игр и т.п.) такой способ работы с клавиатурой неудобен. Поэтому ОС UNIX предоставляет разнообразные и гибкие средства для управления вводом с клавивтуры (в более широком смысле - асинхронными портами ЭВМ). Ниже описывается один из способов обеспечения "мгновенного" ввода кода символа с клавиатуры (без организации очереди символов, замыкаемой клавишой "Ввод") в режиме т.н. неканонического ввода.
  523.         */
  524.        
  525.        
  526.        
  527.         // Второй аргумент - устанавливаем значения флагов в соответствии с 3-м параметром kbh_oldf | O_NONBLOCK
  528.         // printf("O_NONBLOCK: %d\n", O_NONBLOCK);  Вывод: 2048   
  529.         // 3-й аргумент = 2 | O_NONBLOCK.
  530.         // fcntl выполняет различные операции над файловым описателем fd. Рассматриваемая операция определяется значением cmd (cmd - переменная-аргумент этой ф-ии, а не консоль Windows)
  531.         // fcntl - передаем три аргумента: 1-STDIN_FILENO - указатель на поток, 2-command (команда, которую необходимо выполнить), 3-
  532.    
  533.         /*
  534.             Последние 2 строчки кода настраивают неблокирующий режим файлового дескриптора.
  535.             Если данные недоступны, когда мы обращаемся к ним, то системный вызов завершится с возвращаемым значением -1, а в errno будет записано значение EAGAIN (may be error again)
  536.             errno.h — заголовочный файл стандартной библиотеки языка программирования С[1], содержащий объявление макроса для идентификации ошибок через их код.
  537.             errno - number of last error
  538.         */
  539.    
  540.     }
  541. }
  542.  
  543. // OK!
  544. short int InitIOPorts(char restore) {           // argument of this function is variable "restore"
  545.     short int permit61 = -1;            // разрешение на открытие 61-го порта (-1 - обозначает, что порт закрыт, 0 - порт открыт)
  546.     if (restore) {              // если need to restore then...
  547.         ioperm(SPKPORT, 1, 0); // Запретим сами себе разрешённый доступ к 0x61 порту On success, zero is returned. On error, -1 is returned, and errno is set appropriately.             // называется ioperm и позволяет открыть доступ ко всем портам в диапазоне от 0 до 0x3FF hexadecimal (1023 in decimal) в том числе и к 61-му ioperm - set port input/output permissions
  548.         // первый аргумент - начиная с какого порта разрешить/запретить доступ, второй - сколько подряд идущих портов, начиная с номера первого аргумента разрешить или запретить доступ, 3 аргумент - 1 разрешить или 0 запретить
  549.         // iopl - разрешает/запрещает доступ ко всем портам
  550.     }
  551.     else {
  552.         permit61 = ioperm(SPKPORT, 1, 1);  // On success, zero is returned. On error, -1 is returned, and errno is set appropriately.      // Разрешили доступ к 0x61 порту, set permit61 value of ioperm. And if zero is returned then...
  553.                             // int ioperm(unsigned long from, unsigned long num, int turn_on);
  554.     }
  555.     return (permit61);                    // возвращаем разрешение 61-го порта
  556. }
  557.  
  558. // OK!
  559. char CheckArgs(short int argc) {            // Проверка аргументов, поступающих из терминала (командной строки) в программу
  560.     char playmode = ARGPLAY;                // аргумент, отвечающий за воспроизведение, возможно ?   (аргумент равен 1-му)
  561.     if (argc >= 3) playmode = ARGSETTIME;   // если кол-во аргументов равно 3-м, то режиму воспроизведения присваиваем ARGSETTIME(аргумент-присвоить время) (аргумент равен 2-м)
  562. //  if (argc > 3) playmode = ARGSETTIME;    // если кол-во аргументов больше 3-х, то режим воспроизведения = "отображаем wav"  ? (аргумент равен 3-м)  // в новой версии устанавливаем время SETTIME
  563.     return (playmode);                      // возвращаем режим воспроизведения
  564. }
  565.  
  566. // OK!
  567. // InitWavFile - инициализирует wav-файл, открывает его, настраивает указатели. Если не получается, возвращает номер ошибки
  568. int InitWavFile(FILE **fwav, char *filename, wavhead *wh, whfmt **wh_ft) {      // Инициализация wav-файла (аргументы: 1-указатель на указатель wav-файла, 2-название и путь к wav-файлу, 3-указатель на заголовок файла, 4-указатель на указатель секции (чанка), описывающий формат файла)
  569.     // аргументы ф-ии:                             1-указатель на указатель потока, связанного с wav-файлом, 2-имя wav-файла (и, наверно, по совместительству, путь к wav-файлу ???), 3-union-структура wave head (хранит параментры: pcm, fact, fact2), 4-указатель на указатель структуру (тип данных) wave head format, описывающую основные параметры и характеристики wav-файла
  570.  
  571.     int status = 0;             // инициализируем переменную "размер данных" = 0 ???
  572.    
  573.     // Construction do {} while (0) used for checking one of this condition: if..else
  574.     // If one of this condition was satisfied -> then break;
  575.  
  576.     /*
  577.         Функция fopen() открывает файл, имя которого задается параметром fname, и возвращает указатель на поток, связанный с этим файлом. Типы операций, которые разрешено выполнять с файлом, определяются параметром mode. Возможные значения параметра mode приведены в таблице 13.1. Строка символов, которая будет играть роль имени реального файла, должна определять его имя, допустимое в данной операционной системе. Эта строка может включать спецификацию пути, если среда поддерживает такую возможность.
  578.    
  579.         Режим  Назначение
  580.         "r" Открывает текстовый файл для чтения
  581.         "w" Создает текстовый файл для записи
  582.         "а"    Дописывает в текстовый файл
  583.         "rb"    Открывает двоичный файл для чтения
  584.         "wb"    Создает двоичный файл для записи
  585.         "ab"    Дописывает в двоичный файл
  586.         "r+"    Открывает текстовый файл для чтения и записи
  587.         "w+"    Создает текстовый файл для чтения и записи
  588.         "а+"   Открывает текстовый файл для чтения и записи
  589.         "rb+" или "r+b"  Открывает двоичный файл для чтения и записи
  590.         "wb+" или "w+b"  Создает двоичный файл для чтения и записи
  591.         "ab+" или "а+b" Открывает двоичный файл для чтения и записи
  592.        
  593.         Если функция fopen() успешно открыла заданный файл, она возвращает указатель FILE. Если файл открыть не удается, возвращается нулевой указатель.
  594.     */
  595.    
  596.     do {   
  597.         //OK!
  598.         // fopen(filename, "rb")  Открывает двоичный файл для чтения   (rb - read binary)
  599.         if ((*fwav = fopen(filename, "rb")) == NULL) {  // Пытаемся открыть wav-файл по имени. И возвращаем указатель на поток, связанный с этим файлом. Если открыть файл не удалось, то ф-ия fopen возвращает нулевой указатель
  600.             status = EFOPEN;            // Неудалось открыть wav-файл по имени, значит в переменную "status" записываем номер ошибки "ошибка открытия файла"
  601.             break;  // и выходим из проверок
  602.         }
  603.    
  604.         // если указатель на поток, связанный с фалом не нулевой и
  605.         // Функция fread считывает n элементов данных,  каждый длиной  size  байтов,  из  потока  stream  в блок с адресной ссылкой ptr.
  606.         // аргументы fread: 1-куда записывать данные, 2-какова длина одного считываемого блока (в байтах), 3-кол-во считываемых блоков из из потока fwav, 4-из какого потока считывать данные
  607.         /*
  608.             Возвращаемое значение      При успешном завершении функция возвращает количество элементов данных (не байт), реально прочитанных.
  609.              В случае  достижения  конца файла или возникновения ошибки  функция  fread   возвращает   short   count (возможно 0). ?????
  610.         */
  611.        
  612.         // *fwav &&
  613.         // Пытаемся прочитать файл (точнее, поток *fwav, связанный с wav-файлом). Размер одного элемента в потоке равен 1 байту. Всего необходимо прочитать элементов sizeof(whriff) + sizeof(whfmt) + sizeof(whdata), *fwav), которые должны равнятся 44 байт, т.к. это стандартный размер заголовка wav-файла
  614.         // whriff, whfmt, whdata - разделы заголовка wav-файла. Сумма размеров этих разделов дает размер всего заголовка wav-файла
  615.         if (fread(wh, 1, sizeof(whriff) + sizeof(whfmt) + sizeof(whdata), *fwav) == TOPHEADSIZE) {   // sum of sizeof whriff+whfmt+whdata == 44. Ф-ия fread возвращает кол-во байт, которые она прочитала. Если кол-во байт, которые она прочитала совпадает с числом 44 (стандартный размер заголовка wav-фала, то чтение заголовка прошло успешно)
  616.             // Если чтение заголовка прошло успешно, то...
  617.             // Предоставляем ф-ии TopHeadCheck три аргумента: указатель на массив с данными
  618.  
  619.             // data head size = -1;
  620.             *wh_ft = &wh -> pcm.fmt;    // write in pointer wh_ft value of wave-format
  621.         }
  622.         status = 1;
  623.     }
  624.     while(0);
  625.     return(status);     // ????? (в конце строки) возвращаем результат процедуры инициализации wav-файла. Возвращаем либо код ошибки, либо размер данных (каких именно данных: аудио или заголовка или все сразу (и то и другое)) ???????
  626. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement