Драйвер клавіатури реалізує функції музичного синтезатора на клавіатурі для Windows NT 5

[ виправити ] текст може містити помилки, будь ласка перевіряйте перш ніж використовувати.

скачати

Факультет інформатики і систем управління

Кафедра ПЗ ЕОМ та інформаційні технології

РОЗРАХУНКОВО-ПОЯСНЮВАЛЬНА ЗАПИСКА

до курсової роботи на тему:

Драйвер клавіатури, який реалізує функції музичного синтезатора на клавіатурі для Windows NT 5

Зміст

1. Введення

2. Конструкторський розділ

2.1 Постановка завдання

2.2 Архітектура Windows NT 5

2.3 драйверне модель WDM

2.3.1 Функції завантаження / вивантаження драйвера

2.3.2 Функції обробки запитів / переривань

2.4 Пріоритети виконання програмного коду

2.5 Стек клавіатури

2.6 Kernel Streaming

2.7 Опис формату MIDI-даних

2.8 Вибір структури програмного забезпечення

2.8.1 Драйвер-фільтр

2.8.2 Отримання доступу до аудіопристрою

2.8.3 Взаємодія компонент програмного забезпечення

2.9 Алгоритм роботи драйвера-фільтра

2.9.1 Функції завантаження / вивантаження драйвера

2.9.2 Функції пакетів IRP

2.9.3 Функції роботи з аудіо-пристроєм

2.9.4 Схема зберігання музичних параметрів клавіш

2.9.5 Розділення завдання на потоки

3. Технологічний розділ

3.1 Вибір засобів розробки програмного забезпечення

3.2 Встановлення драйвера в системі

3.3 Складання програмного забезпечення

3.4 Використання бібліотеки DirectKS

3.5 Відправлення запитів з програми в драйвер

3.6 Опис інтерфейсу користувача

3.7 Тестування програмного забезпечення

4. Висновок

5. Список літератури

6. Програми

6.1 Функції установки драйвера в системі

6.2 Зміни в бібліотеці DirectKS

1. Введення

У сучасних обчислювальних системах найважливішу роль відіграє підсистема вводу / виводу. Відомо, що приблизно 80% інструкцій програми пов'язані з цією підсистемою. У зв'язку з цим в даний час зростає актуальність розробки програмних систем, що здійснюють взаємодію з периферійними пристроями за допомогою підсистеми вводу / виводу.

Це програмне забезпечення дозволяє організувати настроюється звуковий супровід натискань клавіш на клавіатурі. Існує безліч варіантів його застосування, серед яких можна виділити наступні:

Спрощення роботи з комп'ютером людям зі слабким зором.

Створення MIDI-синтезатора на клавіатурі.

Створення системи звукової сигналізації.

У стандартну поставку ОС Microsoft Windows XP Professional входять компоненти, що забезпечують спеціальні можливості операційної системи для користувачів з різними відхиленнями у здоров'ї. Серед цих компонент слід відзначити:

«Екранну клавіатуру», за допомогою якої, зокрема, можна озвучити вибір клавіш мишею.

Можливість озвучування натисків клавіш Num, Caps і Scroll Lock.

Характерною особливістю вищезазначених компонент є подача сигналу однієї частоти незалежно від натиснутої клавіші і неможливість налаштування параметрів звучання. У зв'язку з цим видається доцільною розробка нового програмного забезпечення, що дозволяє організувати звуковий супровід клавіатури в більш повному обсязі, зокрема, налаштовувати вибіркове озвучування клавіш.

2. Конструкторський розділ

2.1 Постановка завдання

Відповідно до завдання на курсову роботу необхідно розробити програмне забезпечення для вирішення наступних завдань:

Висновок музичних нот з заданими параметрами при натисканні клавіш.

Можливість налаштування користувачем параметрів звучання клавіш.

Програмне забезпечення складається з двох взаємодіючих частин:

Драйвер-фільтр, який реалізує першу задачу.

Програма настройки, що реалізує другу задачу.

Вимоги до програмного забезпечення:

Драйвер-фільтр має відстежувати натискання всіх клавіш клавіатури PS / 2 і генерувати музичні ноти з відповідними клавіші параметрами.

Програма настройки повинна дозволяти користувачеві редагувати параметри звучання клавіш, надаючи зручний інтерфейс.

Як пристрій для виведення музичних нот використовується синтезатор MIDI, вбудований в звукову карту Sound Blaster.

Пріоритетним критерієм при розробці продукту є продуктивність для драйвера-фільтра і зручність користувача для програми установки.

Програмне забезпечення орієнтоване на роботу в ОС Windows NT 5. Функціонування програмного забезпечення не повинно впливати на роботу інших драйверів системи.

2.2 Архітектура Windows NT 5

Архітектура Windows NT 5 відповідає класичним уявленням про проектування операційних систем. Найбільш поширені реалізації даної ОС для платформи Intel x 86 у одно-або багатопроцесорних конфігураціях, проте існують також версії для DEC Alpha і MIPS. Windows NT 5 використовує захищений режим центрального процесора, реалізує механізми віртуальної пам'яті і багатозадачності.

Виконуваний код у Windows NT 5 має два рівні привілеїв: код режиму користувача і код режиму ядра. Рівень привілеїв накладає певні обмеження: у режимі користувача не можуть виконуватися привілейовані інструкції процесора, що не дозволено звернення до захищених сторінок пам'яті. Ці обмеження накладаються для забезпечення безпеки роботи системи. Для користувача додаток не повинно мати можливість в результаті помилки або навмисно вносити зміни в критичні таблиці операційної системи або в пам'ять інших додатків. Зокрема, такі обмеження забороняють користувача додатком безпосередньо керувати зовнішніми пристроями, тому що кожне з них є ресурсом, що розділяється.

У Windows NT 5 забезпечення обміну даними і управління доступом до зовнішнього пристрою як до ресурсу покладається на його драйвер. Введення і виведення в драйверах здійснюється за допомогою IRP-пакетів (Input / Output Request Packet). Запити на введення / висновок, що їх посилають додатками або іншими драйверами, обробляються драйвером, після чого запитуючої програмою в тому ж пакеті надсилається статус завершення операції. Загальний принцип взаємодії проілюстровано на рис. 2.1.

Рис.2.1. Архітектура введення / виведення Windows NT 5

Управління зовнішнім пристроєм в загальному випадку зводиться до заповнення його регістрів необхідними даними. Монопольний доступ драйвера до цих регістрів гарантується операційною системою. Очевидно, що за даних обставин потрібно, щоб драйвер пристрою виконувався в режимі ядра.

Узагальнена класифікація драйверів Windows NT 5 може бути представлена ​​наступним чином:

Драйвери режиму ядра:

Успадковані драйвери

Драйвери файлової системи

Відеодрайвери

Драйвери PnP (Plug And Play):

Драйвери WDM

Драйвери користувальницького режиму:

Драйвери віртуальних пристроїв

2.3 драйверне модель WDM

Windows Driver Model (WDM) - це стандартна специфікація Microsoft для розробників драйверів пристроїв. Вона підтримується в операційних системах Windows 98 / Me / 2000 / XP. Компанія Microsoft вимагає, щоб всі нові драйвери під ці операційні системи створювалися з цієї специфікації. Для цього від них вимагається чітке дотримання структурі WDM, підтримка Plug and Play і управління електроживленням.

Драйверне модель WDM побудована на організації і маніпулюванні шарами об'єктів фізичних пристроїв (PDO, Physical Device Object) і об'єктів функціональних пристроїв (FDO, Functional Device Object).

Об'єкт PDO створюється для кожного фізично ідентифікованого елемента апаратури, підключеного до шини даних, і передбачає відповідальність за низькорівневий контроль, досить загальний для набору функцій реалізованих цим апаратним елементом. Об'єкт PDO - це особлива структура даних, створювана системою для взаємодії програмного і апаратного забезпечення.

Об'єкти FDO увазі одиницю логічної функціональності пристрою.

Об'єктам FDO пристроїв дозволяється оточувати себе Об'єктами Фільтрами (FiDO, Filter Device objects) як показано на рис. 2.2. Відповідно, кожному FiDO об'єкту сопоставлен драйвер, який виконує певну роботу. Об'єкти фільтри підрозділяються на фільтри нижнього рівня та фільтри верхнього рівня. І тих і інших може існувати довільна кількість. Вони модифікують процес обробки запитів вводу / виводу. Об'єкти FDO і FiDO відрізняються тільки в смисловому плані. FDO об'єкт і його драйвер вважаються головними. Вони звичайно забезпечують керування пристроєм, а об'єкти FiDO є допоміжними.

Рис. 2.2. Стек пристроїв

Всі об'єкти FDO і FiDO позиціонують себе в стеку пристроїв. Порядок об'єктів в стеку визначає порядок обробки запитів вводу-виводу. Якщо необхідно перехопити і обробити запит, безпосередньо йде від користувача, то потрібно встановлювати верхній фільтр. Якщо ж потрібно відслідковувати звернення до портів вводу виводу, обробляти переривання, то потрібен нижній фільтр. Дана модель дозволяє драйверу встановлювати callback процедури. Коли запит починає оброблятися, то він обробляється послідовно усіма драйверами стека пристрої (виключаючи ситуацію, коли який-небудь драйвер сам завершить обробку запиту). Після цього диспетчер вводу-виводу передає запит callback процедурі кожного драйвера стека. Спочатку запит передається callback процедурі останнього драйвера який у стеку, потім процедурі передостаннього драйвера і т.д. Callback процедури потрібні для того, щоб обробити прочитану з пристрою інформацію. Якщо фільтр обробляє запити на читання, то коли цей запит надійде в драйвер інформація ще не буде лічена. Тому драйверу необхідно встановити callback функцію. При її виклику запит вже буде містити лічені дані.

Драйвер має такі точки входу:

DriverEntry

DriverUnload

AddDevice

Функції для обробки пакетів IRP

ISR

2.3.1 Функції завантаження / вивантаження драйвера

WDM-драйвери відрізняються від успадкованих драйверів тим, що повинні містити додаткові точки входу для підтримки PnP. Наведемо список точок входу і коротко охарактеризуємо їх призначення.

NTSTATUS DriverEntry (

IN PDRIVER_OBJECT DriverObject, / / покажчик на об'єкт драйвера

IN PUNICODE_STRING RegistryPath) / / шлях до підрозділу регістра,

/ / Відноситься до драйвера

Ця функція виконується при завантаженні драйвера операційною системою. У WDM-драйвери на DriverEntry покладені обов'язки по реєстрації всіх інших точок входу драйвера.

NTSTATUS AddDevice (

IN PDRIVER_OBJECT DriverObject, / / покажчик на об'єкт драйвера

IN PDEVICE _ OBJECT PhysicalDeviceObject) / / покажчик на батьківський PDO

У підтримуючих PnP драйверах через цю точку входу менеджер PnP посилає драйверу повідомлення про виявлення пристрою, за яке має відповідати драйвер. Функція AddDevice повинна створити об'єкт пристрою за допомогою виклику IoCreateDevice і при необхідності приєднати його до стека пристроїв викликом IoAttachDeviceToDeviceStack.

NTSTATUS DriverUnload (

IN PDRIVER_OBJECT DriverObject) / / покажчик на об'єкт драйвера

Викликається при вивантаженні драйвера. У цієї функції повинні звільнятися всі витребувані драйвером ресурси. Драйвери WDM-моделі виконують ці дії в обробнику запитів IRP _ MJ _ PNP з субкод IRP _ MN _ REMOVE _ DEVICE, тобто при видаленні пристрою з системи.

2.3.2 Функції обробки запитів / переривань

Слід виділити окремий клас точок входу драйвера, які призначені для обробки IRP-пакетів з різними кодами операцій. Ці точки входу реєструються при завантаженні драйвера у функції DriverEntry. Реєстрація проводиться шляхом заповнення елементів масиву MajorFunction адресами діспетчерізуемих функцій. Індексом в цьому масиві є коди IRP _ MJ _ XXX, то є описані числами типи пакетів IRP. Диспетчер введення / виведення, орієнтуючись на заповнення цього масиву, викликає потрібні функції драйвера.

Оскільки для драйвера важливі тільки адреси робочих процедур, то всі робочі процедури можуть мати абсолютно довільні імена.

Стандартний прототип таких функцій обробки:

NTSTATUS IRPControl (

IN PDEVICE_OBJECT DeviceObject, / / покажчик на об'єкт пристрої

IN PIRP Irp) / / покажчик на пакет IRP

Функції пакетів IRP

Функції, адреси яких записані в масиві MajorFunctions, викликаються диспетчером вводу / виводу для обробки відповідних запитів від клієнтського драйвера (призначених для користувача додатків або модулів рівня ядра). Ці запити оформляються у вигляді спеціальних структур - IRP пакетів.

При будь-якому запиті Диспетчер формує IRP. Пам'ять для структури IRP виділяється в нестранічной пам'яті. У IRP записується код операції вводу виводу. Пакет IRP складається із заголовка (рис. 2.3), який має постійний розмір і стека IRP (рис. 2.4). Стек має змінну довжину.

Тема IRP пакету:

Поле IoStatus типу IO _ STATUS _ BLOCK містить два підполя

Status - значення, яке встановлює драйвер після обробки пакета.

У Information - частіше всього число переданих або отриманих байт.

Поле AssociatedIrp. SystemBuffer типу void * містить покажчик на системний буфер для випадку якщо пристрій підтримує буферизованная введення / висновок.

Поле MdlAddress типу PMDL містить покажчик на MDL список, якщо пристрій підтримує пряме введення висновок.

Поле UserBuffer типу void * містить адресу користувацького буферу для введення / виводу.

Поле Cancel типу BOOLEAN - це індикатор того, що пакет IRP повинен бути анульований.

Рис. 2.3. Тема IRP-пакету

Стек IRP пакету

Основне значення осередків стека IRP пакету полягає в тому, щоб зберігати функціональний код і параметри запиту на ввід / вивід. Для запиту, який адресований драйверу самого нижнього рівня, що відповідає IRP пакет має тільки одну клітинку стека. Для запиту, який посланий драйверу верхнього рівня, Диспетчер введення / виведення створює пакет IRP з кількома стековими осередками - по одній для кожного FDO.

Кожна клітинка стека IRP містить:

MajorFunction типу UCHAR - це код, що описує призначення операції

MinorFunction типу UCHAR - це код, що описує суб-код операції

DeviceObject типу PDEVICE _ OBJECT - це покажчик на об'єкт пристрою, якому був адресований даний запит IRP

FileObject типу PFILE _ OBJECT - файловий об'єкт для даного запиту

Диспетчер введення / виводу використовує поле MajorFunction для того, щоб витягти з масиву MajorFunction потрібну для обробки запиту процедуру.

Рис. 2.4 Стек IRP-пакету

Функція обробки пакетів IRP _ MJ _ DEVICE _ CONTROL

Ця функція дозволяє обробляти розширені запити від клієнтів користувацького режиму. Такий запит може бути сформований за допомогою виклику функції DeviceIoControl. Кожен IOCTL запит має свій код. Цей код передається як параметр функції DeviceIoControl. Код IOCTL - це 32-бітове число.

Запити IOCTL служать найчастіше для обміну даними між драйвером та програмою. Для передачі даних в Windows передбачені 4 способи:

METHOD _ BUFFERED

Вхідний користувальницький буфер копіюється в системний, а після закінчення обробки системний копіюється в у вихідний користувальницький буфер.

METHOD_IN_DIRECT і METHOD_OUT_DIRECT

Необхідні сторінки користувацького буферу завантажуються з диска в оперативну пам'ять і блокуються. Використовуються MDL-списки для доступу до буфера користувача.

METHOD_NEITHER

При цьому методі передачі не проводиться перевірка доступності пам'яті, не виділяються проміжні буфера і не створюються MDL. У пакеті IRP передаються віртуальні адреси буферів у просторі пам'яті ініціатора запиту вводу / виводу.

Функція обробки пакетів IRP _ MJ _ READ

Ця функція повинна обробляти запити на читання інформації з пристрою.

Функція обробки пакетів IRP _ MJ _ PNP

Ця функція повинна обробляти запити від менеджера PnP.

Функція обробки пакетів IRP _ MJ _ POWER

Ця функція повинна обробляти запити від менеджера харчування.

ISR

Дана точка входу викличеться коли відбудеться переривання, на яке зареєстрована ця ISR функція. Виклик може відбутися в будь-якому контексті: як ядра, так і для користувача процесу. Тут драйвер може або чекати наступного переривання або запросити відкладений виклик процедури DPC (Deferred Procedure Call).

2.4 Пріоритети виконання програмного коду

Переривання вимагає обробки, тому виконання поточного коду припиняється і керування передається обробнику переривання. Існують як апаратні, так і програмні переривання. Переривання обслуговуються у відповідності з їх пріоритетом. Windows NT 5 використовує схему пріоритетів переривань, відому під назвою «рівні запитів переривань» (interrupt request levels, IRQL). Всього існує 32 рівня, з 0 (passive), що має найнижчий пріоритет, за 31 (high), що має відповідно найвищий. Причому, переривання з IRQL = 0 (PASSIVE_LEVEL, рівень нормального виконання потоків) за IRQL = 2 (DISPATCH_LEVEL, планування потоків і виконання відкладених процедур) є програмними, а переривання з IRQL = 3 (device 1) за IRQL = 31 (HIGH_LEVEL, перевірка комп'ютера і шинні помилки) є апаратними. У будь-який конкретний момент часу кожна інструкція виконується на одному певному рівні IRQL. Переривання з рівнем IRQL = 0, строго кажучи, перериванням не є, тому що воно не може перервати роботу ніякого коду (для цього цей код повинен виконуватися на ще більш низькому рівні переривання, а такого рівня немає).

Потоки, що працюють на рівні PASSIVE_LEVEL, потрапляють під управління планувальника завдань (scheduler). Пріоритети, які розрізняє планувальник завдань для потоків з рівнем PASSIVE_LEVEL, приймають значення від 0 до 31 (MAXIMUM_PRIORITY) і називаються пріоритетами планування. Розрізняють Real-Time і Normal пріоритети планування. Перші продовжують свою роботу до тих пір, поки не з'явиться потік з великим пріоритетом, так що потоки низьких пріоритетів повинні чекати, поки поточний потік Real-Time не завершить роботу природним шляхом. Потоки з пріоритетами Normal плануються за іншими правилами. Для роботи їм виділяється певний квант процесорного часу, після чого управління передається іншим потокам такого ж пріоритету. Час від часу планувальник може підвищувати пріоритет відкладеного потоку в межах діапазону Normal, в результаті чого всі програмні потоки серед потоків цієї групи, навіть мають найнижчі пріоритети, рано чи пізно отримують управління.

2.5 Стек клавіатури

Фізичну зв'язок клавіатури з шиною здійснює мікроконтроллер клавіатури Intel 8042. На сучасних комп'ютерах він інтегрований в чіпсет материнської плати. Цей контролер може працювати в двох режимах: AT-сумісному і PS/2-совместімом. Майже всі клавіатури вже давно є PS/2-совместімимі. У PS/2-совместімом режимі мікроконтроллер клавіатури також пов'язує з шиною і PS/2-совместімую миша. Даним мікроконтролером управляє функціональний драйвер i8042prt. Драйвер i8042prt створює два безіменних об'єкта «пристрій» і підключає один до стеку клавіатури, а інший до стека миші. Поверх драйвера i8042prt, точніше, поверх його пристроїв, розташовуються іменовані об'єкти «пристрій» драйверів Kbdclass і Mouclass. Драйвери Kbdclass і Mouclass є так званими драйверами класу і реалізують загальну функціональність для всіх типів клавіатур і мишей, тобто для всього класу цих пристроїв. Обидва ці драйвера встановлюються як високорівневі драйвери.

Стек клавіатури обробляє кілька типів запитів. У цій роботі необхідно розглянути тільки IRP типу IRP_MJ_READ, які несуть з собою коди клавіш. Генератором цих IRP є потік необробленого введення RawInputThread системного процесу csrcc.exe. Цей потік відкриває об'єкт «пристрій» драйвера класу клавіатури для ексклюзивного використання і надсилає йому IRP типу IRP_MJ_READ. Отримавши IRP, драйвер Kbdclass відзначає його як очікує завершення (pending), ставить в чергу і повертає STATUS_PENDING. Потоку необробленого введення доведеться чекати завершення IRP. Підключаючись до стека, драйвер Kbdclass реєструє у драйвера i8042prt процедуру зворотного виклику KeyboardClassServiceCallback, направляючи йому IRP IOCTL_INTERNAL_KEYBOARD_CONNECT. Драйвер i8042prt теж реєструє у системи свою процедуру обробки переривання (ISR) I8042KeyboardInterruptService, викликом функції IoConnectInterrupt. Коли буде натиснута або відпущена клавіша, контролер клавіатури виробить апаратне переривання. Його обробник викличе I8042KeyboardInterruptService, яка прочитає з внутрішньої черги контролера клавіатури необхідні дані. Оскільки обробка апаратного переривання відбувається на підвищеному IRQL, ISR робить тільки саму невідкладну роботу і ставить в чергу виклик відкладеної процедури I8042KeyboardIsrDpc (DPC). DPC працює при IRQL = DISPATCH_LEVEL. Коли IRQL знизиться до DISPATCH_LEVEL, система викличе процедуру I8042KeyboardIsrDpc, яка викличе зареєстровану драйвером Kbdclass процедуру зворотного виклику KeyboardClassServiceCallback (також виконується на IRQL = DISPATCH_LEVEL). KeyboardClassServiceCallback витягне зі своєї черги чекає завершення IRP, заповнить структуру KEYBOARD_INPUT_DATA, несучу всю необхідну інформацію про натиснення / отпусканиях клавіш, і завершить IRP. Потік необробленого введення пробуджується, обробляє отриману інформацію і знову посилає IRP типу IRP_MJ_READ драйверу класу, який знову ставиться в чергу до наступного натискання / відпускання клавіші. Таким чином, у стека клавіатури завжди є, принаймні, один, що очікує завершення IRP, і знаходиться він в черзі драйвера Kbdclass. Стек клавіатури представлений на рис.2.5.

Рис. 2.5. Стек клавіатури

2.6 Kernel Streaming

Kernel streaming (KS) - це сукупність функцій Windows NT 5, які обробляють у режимі ядра потокові дані, такі як аудіо і відео-даними. WDM аудіо-драйвер надає системі свої музичні функції, як набір фільтрів KS. Об'єкт KS фільтра може розширити функції аудиоадаптера, якщо потрібна додаткова цифрова обробка аудіо-потоків, які йдуть через цей фільтр. Наприклад, фільтр може перетворювати формати потоків, синтезувати або змішувати потоки.

Фільтр може містити кілька пінів. Пін є точкою входу чи виходу, через яку аудіо-потік входить або залишає фільтр. У кожного піна є певний формат даних, і дані тільки цього формату можуть проходити через пін.

Піни можуть бути приєднані до пінам інших фільтрів, що дозволяє створювати графи фільтрів. Для того щоб бути частиною графа аудіо-фільтрів, фільтр повинен містити не менше одного Піна.

KS-фільтр - це об'єкт ядра і до нього можна отримати доступ використовуючи HANDLE. Звернення до об'єкта Піна проводиться також з використанням HANDLE. Дані входять у вхідні Піни, проходять відповідну обробку у вузлах фільтра і виходять з виходять пінів, як показано на рис. 2.6.

Рис.2.6. Схема аудіо-фільтра

2.7 Опис формату MIDI-даних

Формат МІДІ - Musical Instrument Digital Interface. Існує 16 міді каналів (0 .. 15). На кожному з них в один і той же час може знаходитися один інструмент.

Усього існує 128 різних інструментів (0 .. 127). Кожен інструмент займає певну позицію в загальній структурі тембрової схеми - таблиця 2.1. Кожен інструмент має 128 нот (0 .. 127).

Найпростіша MIDI-команда складається з 3 байт, які відправляються MIDI-пристрою.

MIDI-команди, які використовуються в даній курсовій роботі:

Утановили інструмент Instrument в каналі Channel

InstrumentByte [0] = 0xC0 | Channel;

InstrumentByte [1] = Instrument;

InstrumentByte [2] = 0;

Відтворити ноту Note в каналі Channel на максимальній гучності

NoteOnByte [0] = 0x90 | Channel;

NoteOnByte [1] = Note;

NoteOnByte [2] = 0 x 7 F;

Вимкнути відтворення ноти Note в каналі Channel

NoteOffByte [0] = 0x80 | Channel;

NoteOffByte [1] = Note;

NoteOffByte [2] = 0x 00;

Таблиця 2.1 Інструменти MIDI

Acoustic Grand Piano

Bright acoustic piano

Electric grand piano

Honky-tonk piano

Electric piano 1

Electric piano 2

Harpsichord

Clavi

Celesta

Glockenspiel

Music box

Vibraphone

Marimba

Xylophone

Tubular bells

Dulcimer

Drawbar organ

Percussive organ

Rock organ

Church organ

Reed organ

Accordian

Harmonica

Tango accordian

Acoustic guitar (nylon)

Acoustic guitar (steel)

Jazz guitar

Clean electric guitar

Muted electric guitar

Overdrive guitar

Distortion guitar

Guitar harmonics

Accoustic bass

Fingered bass

Picked bass

Fretless bass

Slap bass 1

Slap bass 2

Synth bass 1

Synth bass 2

Violin

Viola

Cello

Contrabass

Tremolo strings

Pizzicato strings

Orchestral harp

Timpani

String ensemble 1

String ensemble 2

Synth. strings 1

Synth strings 2

Choir ahh

Choir oohh

Synth voice

Orchestral hit

Trumpet

Trombone

Tuba

Muted trumpet

French horn

Brass section

Synth brass 1

Synth brass 2

Soprano sax

Alto sax

Tenor sax

Baritone sax

Oboe

English horn

Bassoon

Clarinet

Piccolo

Flute

Recorder

Pan flute

Blown bottle

Shakuhachi

Whistle

Ocarina

Square wave

Sawtooth wave

Caliope

Chiff

Charang

Voice

Fifth's

Bass & lead

New age

Warm

Polysynth

Choir

Bowed

Metallic

Halo

Sweep

FX rain

FX soundtrack

FX crystal

FX atmosphere

FX brightness

FX goblins

FX echo drops

FX star theme

Sitar

Banjo

Shamisen

Koto

Kalimba

Bagpipe

Fiddle

Shanai

Tinkle bell

Agogo

Steel drums

Woodblock

Taiko drum

Melodic tom

Synth drum

Reverse cymbal

Guit.fret noise

Breath noise

Seashore

Bird tweet

Telephone ring

Helicopter

Applause

Gunshot

2.8 Вибір структури програмного забезпечення

Реалізація проекту вимагає попереднього вивчення вже існуючих технологій, ретельного відбору методів, що задовольняють всім накладається завданням умов. Відповідно до завдання на курсову роботу, необхідно розробити програмне забезпечення, що дозволяє відтворювати музичні ноти з заданими параметрами при натисканні клавіш. Необхідно вибрати тип і структуру драйвера, який буде отримувати інформацію про натиснутих клавішах. Також необхідно вибрати спосіб доступу до аудіопристрою, здатному відтворювати MIDI-ноти.

2.8.1 Драйвер-фільтр

З аналізу архітектури Windows XP слід, що для доступу до інформації, яка містить коди натиснутих або відпущених клавіш необхідно написати драйвер. Драйвер може отримати доступ до кодів натиснутих або відпущених клавіш двома способами. Або перехоплюючи IRP пакети від інших драйверів, або самостійно обробляючи переривання від клавіатури. Краще вибрати драйвер-фільтр верхнього рівня, оскільки інформація, яка повертається драйвером клавіатури, добре документована і описана в літературі. Розроблюваний драйвер повинен бути WDM-драйвером.

Розроблюваний драйвер-фільтр встановлюється над фільтром Kbdclass. Так як IRP типу IRP_MJ_READ є фактично запитом на читання даних, то коли він йде вниз по стеку, його буфер порожній. Прочитані дані буфер буде містити після завершення IRP. Щоб ці дані побачити, фільтр повинен встановити в свій блок стека IRP процедуру завершення. Місце драйвера-фільтра в стеку клавіатури представлено на рис.2.7.

Рис.2.7. Місце драйвера-фільтра в стеку клавіатури

2.8.2 Отримання доступу до аудіопристрою

Для того щоб відтворювати звук, використовуючи Kernel Streaming, необхідно:

Перебрати всі аудіо-фільтри категорії KSCATEGORY_AUDIO.

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

Вибрати відповідний пін і ініціалізувати об'єкт Піна.

Встановити стан Піна в KSSTATE _ RUN, відправивши IRP-пакет.

Надіслати піну музичні команди IRP-пакетом.

Інтерфейс для роботи з функціями Kernel Streaming в режимі ядра описаний не повністю. Розробники MS Windows рекомендують відтворювати звук тільки з режиму користувача, використовуючи API Windows Multimedia, бібліотеки DirectMusic або DirectSound.

Існує опис інтерфейсу для роботи з фільтрами і пінами в режимі користувача. Дана функціональність надається бібліотекою DirectKS від фірми Microsoft. Вихідні тексти цієї бібліотеки відкриті. У початковому варіанті цієї бібліотеки був реалізований доступ до пристроїв, що відтворює WAVE-дані. Ця бібліотека використовується в даному проекті, і в неї внесені зміни. Реалізовано доступ до пристроїв, що відтворює MIDI-ноти.

У даному проекті пошук потрібного Піна і ініціалізація його об'єкта проводиться в режимі користувача з використанням бібліотеки DirectKS. Далі об'єкт Піна передається в драйвер режиму ядра. Драйвер відправляє музичні команди аудіоприладу, відправляючи IRP-пакети піну.

2.8.3 Взаємодія компонент програмного забезпечення

У рамках даного курсового проекту реалізований драйвер-фільтр і програма, яка керує роботою драйвера. Використовується відкрита бібліотека DirectKS. На рис. 2.8 представлена ​​схема взаємодії компонент проекту, а також процесів і драйверів операційної системи, які беруть участь у роботі цих компонентів.

Рис.2.8. Схема взаємодії компонент розроблюваного проекту

2.9 Алгоритм роботи драйвера-фільтра

Оскільки розробляється драйвер-фільтр є WDM-драйвером, то повинен мати такі точки входу: DriverEntry, DriverUnload, AddDevice, функції для обробки пакетів IRP. Функції для обробки переривань в даній роботі не реєструються, оскільки драйвер не працює з перериваннями.

2.9.1 Функції завантаження / вивантаження драйвера

DiverEntry

Заповнення масиву MajorFunctions.

Реєструється процедура обробки пакета на читання, процедура обробки IOCTL-запитів, процедури обробки запитів від менеджера PnP ​​і менеджера харчування. Інші елементи масиву заповнюються адресою функції __MyFilterDispatchGeneral, яка пропускає пакети нижче по стеку.

Реєстрація процедури AddDevice. У даній роботі вона називається MyFilterAddDevice.

Реєстрація процедури DriverUnload, що називається MyFilterUnload.

AddDevice

У даній роботі функція __MyFilterAddDevice створює одне функціональний пристрій з назвою \ Device \ kbd _ filter. Відбувається резервування місця для зберігання адреси пристрою, розташованого нижче в стеку драйверів. Це зроблено для того, щоб при руйнуванні стека драйверів передати запит PnP на демонтаж нижчестоящому драйверу. Створене пристрій підключається до стека драйверів клавіатури. Це робиться за допомогою функції IoAttachDeviceToDeviceStack. Це стандартна функція Windows, вона приймає PDO і покажчик на структуру підключається FDO. FDO займає місце в стеку драйверів відразу після об'єкту, що знаходиться у вершині стека. Тепер підключається FDO стає вершиною стека. Черговість завантаження драйверів описана в реєстрі Windows.

Для того щоб користувальницький додаток змогло звернутися до драйвера для FDO повинно бути зареєстровано DOS ім'я. Використовуючи це ім'я, додаток зможе послати драйверу IOCTL-запит. Для реєстрації такого імені створюється рядок-юнікод зі значенням \ DosDevices \ kbd _ filter і застосовується функція IoCreateSymbolicLink. Її параметрами є тільки що створена рядок і ім'я FDO, яке обслуговує наш драйвер. Тепер \ DosDevices \ kbd _ filter - це DOS ім'я створеного FDO пристрою.

У цій функції відбувається також ініціалізація інших об'єктів, що використовуються в роботі драйвера-фільтра:

PinInit () - ініціалізація модуля, який працює з піном.

KeyMidiInit () - ініціалізація таблиці, в якій зберігається інформація про музичні параметрах клавіш.

Створення системного потоку PlayThread, який відправляє музичні команди аудіопристрою.

Створення двох черг, які у роботі двох потоків.

Створення об'єктів синхронізації потоків.

DriverUnload

Оскільки даний фільтр є PnP драйвером, то на процедуру DriverUnload нічого не покладено.

2.9.2 Функції пакетів IRP

Розроблюваний драйвер-фільтр здійснює обробку наступних пакетів IRP:

IRP_MJ_DEVICE_CONTROL

IRP_MJ_READ

IRP_MJ_PNP

IRP_MJ_POWER

Решта IRP пакети пропускаються нижче по стеку драйверів.

Функція обробки пакетів IRP _ MJ _ DEVICE _ CONTROL

У даній роботі для користувача додаток повинен мати можливість посилати IOCTL-запити драйверу. Додаток повинен мати можливість відправити драйверу об'єкт відкритого музичного Піна і музичні параметри клавіші.

Для цього в тілі драйвера визначені дві 32-бітові константи:

# Define IOCTL_SHARE_PIN \

CTL_CODE (FILE_DEVICE_KEYBOARD, 0x810, METHOD_BUFFERED, FILE_ANY_ACCESS)

За цим кодом у драйвер передається 4 байти, які є HANDLE об'єкта Піна, відкритого в користувача програмі.

# Define IOCTL_MIDI_NOTE \

CTL_CODE (FILE_DEVICE_KEYBOARD, 0x811, METHOD_BUFFERED, FILE_ANY_ACCESS)

За цим кодом у драйвер передається 7 байт, які можна описати структурою:

typedef struct _ KEY _ MIDI _ INFO

{UCHAR ScanCode; / / Скан-код клавіші, що генерується клавіатурою

UCHAR Flag; / / Прапор клавіші, що генерується клавіатурою

UCHAR Position; / / Позиція на клавіатурі (всього 104 клавіші)

UCHAR Channel; / / Музичний канал

UCHAR Instrument; / / Музичний інструмент

UCHAR Note; / / Музична нота

UCHAR Used; / / Для клавіші використовується нота чи ні

} KEY_MIDI_INFO, * PKEY_MIDI_INFO;

Це коди IOCTL-запитів, які не використовуються драйверами стека клавіатури. Тому в даному проекті вони можуть бути використані без жодних побоювань.

Застосовується спосіб передачі даних METHOD _ BUFFERED. Оскільки передається буфер в 4 або 7 байт, то його розмір не зашкодить системного пулу, і копіюватися користувальницький буфер в системний буде дуже швидко. Немає необхідності застосовувати більш складні методи METHOD _ IN _ DIRECT або METHOD _ NEITHER, які використовуються при передачі великих обсягів даних.

Функція обробки пакетів IRP _ MJ _ READ

Дана функція здійснює обробку пакетів на читання. IRP-пакет спочатку попадає в розроблювальний драйвер. Викличеться зареєстрована в DriverEntry функція __MyFilterDispatchRead. До моменту виклику __MyFilterDispatchRead, буфер не містить кодів лічених клавіш. Для того щоб отримати доступ до них __MyFilterDispatchRead повинна встановити CallBack процедуру __MyFilterReadComplete. Вона отримає управління, коли буфер IRP-пакета буде містити інформацію про натиснутих клавішах. Пакет буде підніматися вгору по стеку драйверів і викликати CallBack функції на кожному рівні стека. CallBack процедура встановлюється за допомогою функції IoSetCompletionRoutine.

MyFilterReadComplete отримує буфер натиснутих клавіш, як масив структур KEYBOARD _ INPUT _ DATA, функція виконується на IRQL <= DISPATCH_LEVEL.

Далі в MyFilterDispatchRead відбувається копіювання поточної комірки IRP-пакета в наступну клітинку. Таким чином відбувається передача незмінених параметрів у Kbdclass.

Функція обробки пакетів IRP _ MJ _ PNP

Драйвер-фільтр повинен обробляти тільки запити IRP_MN_REMOVE_DEVICE і IRP_MN_SURPRISE_REMOVAL. При цьому функція посилає даний пакет менеджера PnP ​​нижчестоящому в стеку пристрою. У обробниках цих запитів відбувається звільнення пам'яті, яка виділялася для модуля роботи з піном, для таблиці музичних нот, відбувається завершення роботи музичного потоку, звільнення черг і об'єктів синхронізації, використовуваних в драйвері.

У обробнику IRP_MN_REMOVE_DEVICE додатково відбувається:

відключення пристрою від стека драйверів викликом функції IoDetachDevice,

видалення пристрою FDO викликом функції IoDeleteDevice,

видалення символьної посилання викликом IoDeleteSymbolicLink.

Решта пакетів пропускаються нижче по стеку.

Функція обробки пакетів IRP _ MJ _ POWER

Оскільки розроблюваний драйвер є фільтром, завдання якого - отримати інформацію про натиснутих клавішах, то в ньому не проводиться жодних дій, пов'язаних зі зміною харчування. Тому ці IRP-пакети пропускаються нижче по стеку.

Обробка інших пактів IRP

Решта IRP-пакети, які не обробляються в даному фільтрі, пропускаються нижче по стеку. Функції даного драйвера-фільтра не в праві самостійно обробляти ці запити, тому що це можуть запити, адресовані нижчестоящим драйверам. Прикладом одного з таких запитів є IOCTL-запит, адресований драйверу i8042prt і призначений для перепрограмування котроллер клавіатури і для запалювання лампочок на клавіатурі.

У даній роботі за пропущення пакетів вниз відповідає процедура __MyFilterDispatchGeneral. Вона передає IRP пакет нижчестоящому драйверу за допомогою функції IoCallDriver. При цьому нижчий драйвер повинен зчитувати поточну клітинку IRP пакета. Це досягається за рахунок використання функції IoSkipCurrentIrpStackLocation.

2.9.3 Функції роботи з аудіо-пристроєм

У модулі midi _ pin реалізовані всі функції, які забезпечують роботу з музичним піном на рівні ядра. У роботі з музичним піном використовуються запити на встановлення стану Піна та відправлення пакетів з музичними даними.

NTSTATUS PinInit ()

У цій функції відбувається ініціалізація полів заголовка IRP-пакета і MIDI-даних, які використовуються під час відправлення музичних команд в пін. Далі у функціях PinMidiNoteOn і PinMidiNoteOff використовується ініціалізований заголовок.

Ініціалізація заголовка IRP-пакету відбувається наступним чином:

typedef __declspec (align (16)) struct _MIDI_DATA

{KSMUSICFORMAT InstrumentFormat; / / include <ksmedia.h>

union

{UCHAR InstrumentByte [4];

UCHAR NoteOffByte [4];};

KSMUSICFORMAT NoteFormat;

UCHAR NoteOnByte [4];

} MIDI_DATA, * PMIDI_DATA;

MIDI_DATA PinMidiData;

KSSTREAM_HEADER PinWriteHeader; / / include <ks.h>

RtlZeroMemory (& PinWriteHeader, sizeof (PinWriteHeader));

/ / 12 байт

PinMidiData.InstrumentFormat.TimeDeltaMs = 0;

PinMidiData.InstrumentFormat.ByteCount = 3;

PinMidiData.InstrumentByte [0] = 0x00;

PinMidiData.InstrumentByte [1] = 0x00;

PinMidiData.InstrumentByte [2] = 0x00;

PinMidiData.InstrumentByte [3] = 0x00;

/ / Ще 12 байт

PinMidiData.NoteFormat.TimeDeltaMs = 0;

PinMidiData.NoteFormat.ByteCount = 3;

PinMidiData.NoteOnByte [0] = 0x00;

PinMidiData.NoteOnByte [1] = 0x00;

PinMidiData.NoteOnByte [2] = 0x00;

PinMidiData.NoteOnByte [3] = 0x00;

PinWriteHeader.Size = sizeof (PinWriteHeader);

PinWriteHeader.TypeSpecificFlags = 0;

PinWriteHeader.PresentationTime.Time = 0;

PinWriteHeader.PresentationTime.Numerator = 1;

PinWriteHeader.PresentationTime.Denominator = 1;

PinWriteHeader.Duration = 0;

PinWriteHeader.FrameExtent = 24; / / всього 24 байта

PinWriteHeader.DataUsed = 24; / / всього 24 байта

PinWriteHeader.Data = & PinMidiData;

NTSTATUS PinOpenStream (IN HANDLE UserPin)

Коли через запит IOCTL _ SHARE _ PIN драйвер отримує об'єкт відкритого Піна, то необхідно викликати цю функцію. У ній відбувається виклик функції ObReferenceObjectByHandle для того, щоб отримати покажчик на об'єкт Піна в режимі ядра і збільшити кількість посилань на об'єкт. Це робиться для того, щоб об'єкт Піна не був видалений з таблиці об'єктів ОЗ після заверешенія роботи призначеного для користувача програми, в якому був створений об'єкт Піна. Також тут відбувається установка прапора, що пін відкритий для драйвера.

UserPin - HANDLE того піна, який міститься в буфері IRP-пакета.

NTSTATUS PinIsOpenedStream ()

Повертає STATUS_SUCCESS якщо пін відкритий.

NTSTATUS PinFree ()

Тут відбувається виклик функції ObDereferenceObject, яка зменшує кількість посилань на об'єкт Піна.

NTSTATUS PinSetState (IN KSSTATE State)

Встановлює стан Піна в State (KSSTATE_RUN, KSSTATE_PAUSE або KSSTATE_STOP). Перед тим, як відтворювати ноти, необхідно встановити стан Піна в KSSTATE _ RUN. Функція працює тільки при IRQL = PASSIVE_LEVEL.

NTSTATUS PinWriteData (IN KSSTREAM_HEADER * Pheader)

Відправляє заголовок у відкритий пін допомогою IOCTL_KS_WRITE_STREAM.

Функція працює тільки при IRQL = PASSIVE _LEVEL.

NTSTATUS PinMidiNoteOn (IN UCHAR Channel,

IN UCHAR Instrument, IN UCHAR Note)

Якщо пін відкритий, то відправляє команду на відтворення ноти Note з використанням інструменту Instrument в каналі Channel.

Модифікація ініціалізованої заголовка:

PinMidiData. InstrumentByte [0] = 0 xC 0 | Channel;

PinMidiData.InstrumentByte [1] = Instrument;

PinMidiData.NoteOnByte [0] = 0x90 | Channel;

PinMidiData.NoteOnByte [1] = Note;

PinMidiData.NoteOnByte [2] = 0x7F;

/ / 24 байта відправляємо, тому що в одному

PinWriteHeader. FrameExtent = 24; / / пакеті 2 команди: ми встановлюємо

PinWriteHeader. DataUsed = 24; / / інструмент і відправляємо ноту

PinWriteData (& PinWriteHeader);

Функція працює тільки при IRQL = PASSIVE _ LEVEL.

NTSTATUS PinMidiNoteOff (IN UCHAR Channel, IN UCHAR Note)

Вимикає ноту Note в каналі Channel.

Модифікація ініціалізованої заголовка:

PinMidiData. NoteOffByte [0] = 0 x 80 | Channel;

PinMidiData. NoteOffByte [1] = Note;

PinWriteHeader. FrameExtent = 12; / / 12 байт шолом, тому що в одному пакеті

PinWriteHeader. DataUsed = 12; / / 1 команда: вимикаємо ноту

PinWriteData (& PinWriteHeader);

Функція працює тільки при IRQL = PASSIVE _ LEVEL.

2.9.4 Схема зберігання музичних параметрів клавіш

Під час натискання клавіші клавіатура генерує скан-код і прапор (ця інформація знаходиться в структурі KEYBOARD _ INPUT _ DATA).

Потрібно організувати таку схему зберігання музичних даних клавіш, щоб під час отримання скан-коду і прапора кнопки можливо було швидко знайти її музичні параметри.

Для кількох клавіш може бути згенерований один і той же скан-код, наприклад для лівого і правого Shift. Їх можливо розрізнити лише за прапором. Не більше ніж трьом клавішах може сооответствовать один скан-код. Тому скан-код не можна використовувати як індекс масиву, в якому зберігаються музичні дані тільки для однієї клавіші - ці музичні дані будуть використовуватися для декількох клавіш з однаковим скан-кодом.

Тоді потрібно для кожної пари (скан-код, прапор) створити новий скан-код клавіші такий, щоб для кожної клавіші існував єдиний скан-код. Позиція - нехай так називається новий скан-код.

Схема реалізована в модулі keys.

Схема така:

typedef struct _KEY_SCANS_TABLE

{UCHAR Usage;

UCHAR ScanMake;

UCHAR ScanE0;

UCHAR ScanE1;

} KEY_SCANS_TABLE, * PKEY_SCANS_TABLE;

typedef struct _KEY_MIDI_TABLE

{UCHAR Used;

UCHAR Channel;

UCHAR Instrument;

UCHAR Note;

} KEY_MIDI_TABLE, * PKEY_MIDI_TABLE;

Створюється масив структур KEY _ SCANS _ TABLE, де індекс масиву - реальний скан-код, що генерується клавіатурою, а в полях ScanMake, ScanE 0, ScanE 1 зберігається позиція клавіші, відповідна того чи іншого значенням прапора клавіші, що генерується клавіатурою. У полі Usage число одиничних біт дорівнює числу встановлених клавіш, що відповідають одному скан-коду.

Створюється масив структур KEY _ MIDI _ TABLE, де індекс масиву - позиція клавіші, в полях структури міститься музична інформація про клавіші:

Used - використовується у схемі чи ні. Якщо використовується: натиснута або відпущена.

Channel - канал, в якому звучить нота. Існує 16 каналів - 0 .. 15.

Instrument - інструмент ноти. Існує 128 інструментів - 0 .. 127.

Note - нота. Існує 128 нот для кожного інструменту - 0 .. 127.

Коли клавіатура під час натискання клавіші генерує скан-код і прапор, то по першій таблиці з використанням скан-коду в якості індексу масиву швидко знаходиться осередок з трьома можливими позиціями. За значенням прапора ціною максимум трьох порівнянь знаходиться потрібна позиція. З використанням позиції в якості індексу другої таблиці швидко знаходяться музичні дані, відповідні самій клавіші.

На звичайній клавіатурі знаходиться 104 клавіші. Тому в даній курсовій роботі використовується 104 позиції (0 .. 103).

Функції установки та вибірки музичної інформації

NTSTATUS KeyMidiInit ()

Виділяє пам'ять для двох таблиць і заповнює їх нулями.

NTSTATUS KeyMidiFree ()

Звільняє пам'ять, виділену для таблиць.

NTSTATUS KeyMidiSetNote (IN UCHAR ScanCode, IN UCHAR Flag,

IN UCHAR Position,

IN UCHAR Channel, IN UCHAR Instrument,

IN UCHAR Note, IN UCHAR Used)

Встановлює в першій таблиці для скан-коду і прапора позицію. Для позиції в другій таблиці встановлює музичні параметри.

NTSTATUS KeyMidiGetPosition (IN UCHAR ScanCode, IN UCHAR Flag,

OUT PUCHAR Position)

За скан-коду та прапору, що згенерував клавіатурою, отримує позицію, використовуючи першу таблицю.

NTSTATUS KeyMidiGetNote (IN UCHAR Position, PUCHAR Channel,

OUT PUCHAR Instrument, OUT PUCHAR Note,

OUT PUCHAR Used)

Використовуючи позицію клавіші, отримує музичні дані з другої таблиці.

NTSTATUS KeyMidiSetUsed (IN UCHAR Position, IN UCHAR Used)

Використовуючи позицію клавіші, встановлює стан клавіші KEY_RELEASED (відпущена) або KEY_PRESSED (натиснута).

2.9.5 Розділення завдання на потоки

Функція MyFilterReadComplete, яка отримує буфер натиснутих клавіш як масив структур KEYBOARD _ INPUT _ DATA, виконується на IRQL <= DISPATCH_LEVEL.

Функція PinMidiNoteOn працює тільки при IRQL = PASSIVE _LEVEL.

Це значить, що функцію PinMidiNoteOn не можна викликати усередині функції MyFilterReadComplete.

У драйвері створений системний потік PlayThread, який відправляє музичні команди аудіопристрою. В основному потоці драйвера функція MyFilterReadComplete заповнює чергу натиснутих клавіш. Далі ця функція сигналізує про подію, що можна починати відтворення нот. За цієї події пробуджується потік PlayThread. Виникає проблема роботи двох потоків з поділюваним ресусрсом - чергою. Тому використовується 2 черги.

Принцип синхронізації потоків

Функція MyFilterReadComplete:

(Потік 1, виконується на IRQL <= DISPATCH_LEVEL)

if (PlayCompleted)

{Приєднати Черга Б до Черги А

Спустошити Черга Б

for (всі натиснуті чи відпущені клавіші)

{Додати в Черга А позицію клавіші}

Встановити Подія PlayEvent в стан «Сигнал»}

else

{For (всі натиснуті чи відпущені клавіші)

{Додати в Черга Б позицію клавіші}}

Функція PlayThread:

(Потік 2, виконується на IRQL = PASSIVE _LEVEL)

Чекати поки Подія PlayEvent не перейде в стан «Сигнал»

Встановити Подія PlayEvent в стан «Сигнал»

/ / Встановлює значення 0 змінної PlayCompleted

/ / Забезпечує монопольний доступ до цієї змінної апаратними засобами

InterlockedExchange (& PlayCompleted, 0);

PinSetState (KSSTATE _ RUN);

while (Черга А не порожня)

{Витягти з Черги А інформацію про натиснутоюклавіші

if (стан на клавіатурі = натиснута і

музичне стан = відпущена)

{Відтворити ноту

Музичне стан = натиснута}

else if (стан на клавіатурі = відпущена і

музичне стан = натиснута)

{Вимкнути ноту

Музичне стан = відпущена}}

PinSetState (KSSTATE_PAUSE);

PinSetState (KSSTATE_STOP);

InterlockedExchange (& PlayCompleted, 1);

Оскільки перший потік виконується на більш високому IRQL, то він може перервати виконання другого потоку в будь-який момент. Наприклад, це може відбутися до того, як другий потік завершить обробку Черги А. У цьому випадку перший потік заповнює Черга Б. Як тільки другий потік закінчить свою попередню операцію і встановить прапор PlayCompleted, то перший потік під'єднати Черга Б до Черги А, додасть нові елементи до Черги А і сигналізує другого потоку про те, що можна почати нову музичну обробку. Другий потік отримає управління не відразу, а як тільки перший потік, що працює на більш високому рівні IRQL, повністю завершить свою роботу.

Функція InterlockedExchange забезпечує монопольний доступ до змінної того потоку, який їх викликав. Всі інші потоки, які використовують цю змінну в цей же час, будуть чекати поки до кінця не відпрацює InterlockedExchange. Використання цієї функції можливе на будь-якому рівні IRQL, тому що вона реалізована апаратно.

Даний механізм забезпечує роботу першого потоку без очікування другого. Тобто відбувається швидка обробка повертається з стека IRP-пакета, натиснувши клавіші. За сигналами з першого потоку другий потік виробляє музичну обробку.

3. Технологічний розділ

3.1 Вибір засобів розробки програмного забезпечення

З двох мов, що використовуються для програмування драйверів, С і асемблера, вибір варто зупинити на С з огляду на те, що це мова високого рівня, тобто ця мова забезпечує рівень абстракції вище, ніж асемблер, що безумовно великий плюс. Крім того, ця мова використовується фірмою Microsoft в найбільш поширеному пакеті для розробки драйверів MS DDK. Ще одним плюсом З є те, що разом з пакетом DDK поставляється спеціальний компілятор для С, призначений для компіляції драйверів.

Орієнтований на мову С, набір DDK включає в себе довідники, всі необхідні бібліотеки та заголовки. Тому була використана технологія структурного програмування. У даній роботі застосовується DDK для Windows Server 2003.

Для редагування вихідних текстів застосовується середу Microsoft Visual Studio. NET і входить до неї компілятор Microsoft Visual C + +.

При складанні виконуваного (з розширенням. Sys) файлу використовується утиліта build, що входить до складу DDK.

При розробці керуючого програми використовується середу Microsoft Visual Studio. NET з візуальним редактором форм.

У проекті використовується бібліотека DirectKS, яка є прикладом отримання доступу до фільтрів Kernel Streaming в режимі користувача. У бібліотеку внесені зміни. Використання бібліотеки описано нижче.

Операційною системою, в якій працює драйвер, є Windows NT 5, тобто Windows 2000, Windows XP, Windows Server 2003.

Оскільки драйвер працює з аудіопристроєм безпосередньо, то на комп'ютері повинна бути встановлена ​​така звукова карта, яка підтримує запити і формат даних, які їй відправляє драйвер. Звукові карти, починаючи з Creative Sound Blaster Live підтримують запити, які використовуються у цьому курсовому проекті.

3.2 Встановлення драйвера в системі

Для установки драйвера необхідно викликати функції драйвера в певний момент завантаження системи. Це необхідно для того, щоб драйвер зайняв потрібне місце в стеку драйверів. Операційна система Windows здійснює завантаження драйверів в порядку, прописаному в системному реєстрі.

Кожен пристрій має свій розділ в реєстрі. Всі ці розділи перебувають в HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControleSet \ Control \ Class. Клавіатурі відповідає розділ {4 D 36 E 96 B - E 325-11 CE - BFC 1-08002 BE 10318}. У кожного пристрою в його розділі є ключі UpperFilters і LowerFilters. Це ключі типу MultiString. Вони містять імена верхніх і нижніх драйверів-фільтрів даного пристрою. Драйвери-фільтри завантажуються в систему в тому порядку, в якому вони записані в цих ключах.

Для реєстрації разрабативавемого драйвера як фільтру необхідно помістити його ім'я в останнім в ключі UpperFilters.

Для реєстрації нового драйвера необхідно створити розділ з ім'ям цього драйвера в системному реєстрі за адресою HKEY _ LOCAL _ MACHINE \ SYSTEM \ CurrentControlSet \ Services. Цей розділ повинен містити наступні ключі:

Type типу подвійне слово

Визначає тип модуля.

Цікавить тільки значення SERVICE_KERNEL_DRIVER (1).

Start типу подвійне слово

Визначає спосіб завантаження драйвера. Може приймати одне з наступних значень:

SERVICE _ BOOT _ START (0) - під час початкового завантаження ОС. Дане значення застосовується, коли драйвер використовується завантажувачем системи;

SERVICE _ SYSTEM _ START (1) - після початкового завантаження ОС. Застосовується для драйверів, які самостійно здійснюють пошук обладнання, але не використовуються завантажувачем системи (нумератори).

SERVICE _ AUTO _ START (2) - автоматичне завантаження з допомогою диспетчера управління сервісами (Service Control Manager). Застосовується для драйверів, що не підтримують Plug and Play.

SERVICE _ DEMAND _ START (3) - завантаження «на вимогу» або диспетчера Plug and Play при виявленні пристрою, або диспетчера управління сервісами при надходженні від користувача команди на завантаження.

SERVICE _ DISABLED (4) - драйвер не завантажується.

ErrorControl типу подвійне слово

Визначає рівень контролю помилок. Може приймати значення:

SERVICE _ ERROR _ IGNORE (0). У разі збою при завантаженні драйвера ігнорувати його і продовжити роботу.

SERVICE _ ERROR _ NORMAL (1). У разі збою при завантаженні драйвера продовжити роботу, але видати при цьому відповідне попередження.

SERVICE _ ERROR _ SEVERE (2). У разі збою при завантаженні драйвера перемкнутися на останню конфігурацію, при якій робота системи відбувалася без помилок.

SERVICE _ ERROR _ CRITICAL (3). Аналогічно (2), але в разі повторного збою видати «синій екран».

DisplayName типу рядок ASCII

Екрана ім'я, що відображається службовими програмами на екрані. Цей параметр не обов'язковий. При його відсутності екранним ім'ям вважається ім'я підрозділу.

ImagePath типу рядок Unicode

Повний шлях до файлу з драйвером на диску. Зазвичай це поле встановлюється в значення% windir% \ system32 \ Drivers \ DriverName.sys, де% windir% - папка, в яку встановлена ​​Windows, а DriverName - ім'я файлу.

Для розроблювального драйвера створюється розділ з ім'ям kbd_filter зі змінними ErrorControl (дорівнює 1), Start (дорівнює 3), Type (дорівнює 1). Оскільки не вказується, де розташований виконуваний файл драйвера, цей файл має бути скопійований в директорію \% SystemRoot% \ System32 \ Drivers.

У розділі фільтрів клавіатури необхідно внести в змінну UpperFilters ім'я драйвера-фільтра.

Після перезавантаження зміни вступлять в силу і драйвер-фільтр почне роботу.

Функції установки і видалення драйвера реалізовані в модулі installer, який входить до складу керуючого додатки.

3.3 Складання програмного забезпечення

Для складання розробленого програмного забезпечення потрібна ОС Windows NT 5, встановлена ​​на комп'ютері звукова карта Sound Blaster. Всі розроблені компоненти розробленого програмного забезпечення перебувають у папці DriverControl.

Компіляція

Для того, щоб скомпілювати драйвер, необхідний встановлений DDK для Windows NT 5. За допомогою консольної утиліти build, що входить до складу DDK, необхідно увійти в папку DriverControl \ driver і ввести команду build. Відбудеться компіляція драйвера. Драйвер kbd _ filter. Sys опиниться в папці DriverControl \ driver \ objchk _ wnet _ x 86 \ i 386.

Для того, щоб скомпілювати керуючу програму, необхідно щоб був встановлений пакет розробки Microsoft Visual Studio. Net. Результатом компіляції є файл DriverControl. Exe в папці DriverControl / Release.

Збірка проекту

Щоб зібрати проект (драйвер і керуюча програма) необхідно скопіювати в будь-яку папку файл DriverControl. Exe, створити в цій папці іншу папку driver і помістити в неї файл kbd _ filter. Sys.

Установка програмного забезпечення

Для того, щоб встановити розроблене програмне забезпечення на комп'ютері, необхідно запустити програму DriverControl. Exe. На вкладці «Установка драйвера» натиснути на кнопку «Встановити». Для того, щоб зміни в системі вступили в силу, необхідно перезавантажити комп'ютер.

Встановлений драйвер можна побачити за допомогою утиліти devicetree, що входить до складу DDK.

3.4 Використання бібліотеки DirectKS

Бібліотека DirectKS надає функціональність для роботи з інтерфейсом Kernel Streaming в режимі користувача. У початковому варіанті DirectKS працює з пристроями, що підтримують формат WAVE-даних. Завданням даної курсової роботи є робота з MIDI-пристроями. Тому в бібліотеку були внесені зміни. Бібліотека знаходиться у файлі DriverControl / directks. Всі внесені зміни описані в Додатку (див. пункт 6.2).

Бібліотека DirectKS використовується наступним чином:

CKsAudRenPin * OpenMidiPin ()

{HRESULT hr;

CKsAudRenFilter * pFilter = NULL;

CKsAudRenPin * pPin = NULL;

CKsEnumFilters * pEnumerator = new CKsEnumFilters (& hr);

GUID aguidEnumCats [] = {STATIC_KSCATEGORY_AUDIO};

/ / Знайти всі фільтри категорії STATIC_KSCATEGORY_AUDIO

/ / Створить списки всіх пінів цих фільтрів

hr = pEnumerator-> EnumFilters (eAudRen, aguidEnumCats, 1, TRUE, FALSE, TRUE);

if (SUCCEEDED (hr))

{/ / Перелічити всі фільтри і Піни, поки не буде знайдений

/ / Пін потрібного формату даних - формату MIDI

LISTPOS listPos = pEnumerator-> m_listFilters.GetHeadPosition ();

while (! pPin & & pEnumerator-> m_listFilters.GetNext (listPos, (CKsFilter **) & pFilter))

{PPin = pFilter-> CreateRenderPin (FALSE);}}

delete pFilter;

return pPin;}

Якщо пін з потрібним форматом даних виявлено, то функція повертає покажчик на клас для роботи з піном.

Для того щоб отримати HANDLE Піна, потрібно використовувати властивість:

Pin-> GetHandle ()

3.5 Відправлення запитів з програми в драйвер

key _ midi - модуль відправлення запитів з програми в драйвер, знаходиться в папці driverio.

Цей модуль містить таблицю відповідностей позиції клавіші до парі (скан-код, прапор).

# Define KEY_MIDI_FILE "C: \ \ KeyMidi.dat"

У цьому файлі дублюється інформація про музичні параметрах, призначених для клавіш.

HRESULT KeyGetScanCode (OUT PUCHAR ScanCode, OUT PUCHAR Flag,

IN UCHAR Position);

По позиції клавіші визначається її скан-код і прапор.

HRESULT KeySaveNote (IN LPCTSTR FileName, IN UCHAR ScanCode,

IN UCHAR Flag, IN UCHAR Position,

IN UCHAR Channel, IN UCHAR Instrument, IN UCHAR Note,

IN UCHAR Used);

Зберігає у файлі FileName музичну інформацію про клавіші.

HRESULT KeyGetNote (IN LPCTSTR FileName,

OUT PUCHAR ScanCode, OUT PUCHAR Flag, IN UCHAR Position,

OUT PUCHAR Channel, OUT PUCHAR Instrument,

OUT PUCHAR Note);

По позиції клавіші отримує з файлу FileName її музичну інформацію.

HRESULT KeyDriverSendNote (IN UCHAR ScanCode, IN UCHAR Flag,

IN UCHAR Position, IN UCHAR Channel,

IN UCHAR Instrument, IN UCHAR Note,

IN UCHAR Used);

Зберігає в драйвері музичну інформацію про клавіші.

HRESULT KeyDriverSendAllNotes (IN LPCTSTR FileName);

Зберігає в драйвері музичну інформацію про всі клавішах, музична інформація про яких збережена у файлі FileName.

HRESULT SharePinWithDriver (IN HANDLE Pin);

Відправляє в драйвер HANDLE відкритого Піна.

3.6 Опис інтерфейсу користувача

Установка

Для того щоб встановити або видалити драйвер необхідно відкрити програму DriverControl. Exe. Вибрати вкладку «Установка драйвера». Кнопки «Встановити» і «Видалити» означають установку або видалення драйвера з системи (рис. 3.1). Для того, щоб зміни набрали чинності, необхідно перезавантажувати комп'ютер.

У разі, якщо на комп'ютері не знайдені потрібні аудіопристрої, буде виведено повідомлення «Неможливо відкрити MIDI-пін». У цьому випадку драйвер встановити можна буде, але призначити ноти для клавіші буде не можна.

Рис.3.1. Установка драйвер

Управління

Для того щоб можна було керувати драйвером, потрібно вибрати в програмі DrivcrControl. Exe вкладку «Призначення звуку». Вибираючи кнопку, яка відповідає клавіші клавіатури, можна для неї призначити канал, інструмент і ноту. Можна заздалегідь перевірити звучання ноти, а потім зберегти музичну інформацію в драйвері. Інформація буде продубльована у файлі C: \ KeyMidi.dat.

Рис.3.2. Управління драйвером

Автозавантаження

Під час перезавантаження операційної системи всі значення змінних, збережені в пам'яті драйвера, втрачаються. Тобто драйверу необхідно чекати користувальницький додаток, яке відправить йому об'єкт Піна і заново призначить ноти для клавіш.

Для цього було створено просте додаток, що відкриває пін, відправляє його в драйвер, відправляє у драйвер інформацію про всі останні музичні параметрах, созраненних у файлі C: \ KeyMidi. Dat.

Проект цього додатка знаходиться в папці DriverControl під назвою DriverAutoloader. Скомпільовану програму DriverAutoloader. Exe можна знайти в папці DriverControl / Release. Цю програму за бажанням можна зберегти в папці Startup, додатки з якої завантажуються під час завантаження ОС Windows.

3.7 Тестування програмного забезпечення

Драйвер був протестований з використанням стандартної тестуючої утиліти DriverVerifier пакету DDK. З його допомогою були проведені наступні тести:

Операції з пулами пам'яті

Коректність рівнів IRQL, на яких виконується код драйвера

Виявлення взаімоблокіровок

Брак ресурсів

Нетипові запити до драйвера

Всі тести пройшли успішно. Пам'ять в системі розподілялася правильно, помилок з нею не виникало. На брак ресурсів драйвер реагував коректно. Нетипові запити до драйвера не оброблялися ім.

Після установки драйвера і призначення музичних параметрів для клавіш робота драйвера триває без «синього екрану».

На комп'ютерах, де не встановлені звукові карти Sound Blaster клавіші не видають звуків, тому що вбудовані звукові карти не підтримують тих IOCTL-запитів, які використовуються під час роботи з об'єктом Піна.

Тестування відбувалося на ОС Windows XP і Windows Server 2003.

4. Висновок

Досліджено структуру драйверів Windows

Визначено місце драйвера в стеку драйверів і обрана структура програмного забезпечення, що складається з двох частин:

- Верхнього драйвера-фільтра клавіатури

- Програми, яка здійснює настройку параметрів музичного озвучування клавіш і управляє роботою драйвера допомогою IOCTL-запитів

Вивчено механізми:

- Вбудовування драйвера-фільтра в стек драйверів

- Взаємодії компонент програмного забезпечення через IOCTL-запити

Розроблено механізми:

- Пошуку доступу до аудіопристрою в режимі ядра

- Синхронізації потоків, що працюють на різних рівнях IRQL

Розроблено програмне забезпечення відповідно до технічного завдання та проведено його тестування.

Розроблений програмний продукт повністю задовольняє поставленому завданню і здійснює настроюється озвучування натискань клавіш на клавіатурі під управлінням ОС Microsoft Windows NT 5.

5. Список літератури

  1. Oney W. Programming the Microsoft Windows Driver Model .- Redmond, Washington: Microsoft Press., 1999.

  2. Форум розробників MS Windows на сайті microsoft.com.

  3. Розсилка, присвячена розробці аудіодрайвер на сайті wdmaudiodev.de

  4. Розсилка, присвячена розробці драйверів для Windows на сайті osronline.com

  5. Microsoft Windows Server 2003 DDK Documentation.

6. Програми

6.1 Функції установки драйвера в системі

# Define DRIVER_KEY "System \ \ CurrentControlSet \ \ Services \ \" DRIVER_NAME

# Define FILTER_KEY "System \ \ CurrentControlSet \ \ Control \ \ Class \ \ {4D36E96B-E325-11CE-BFC1-08002BE10318}"

char * SearchString (char * Value, DWORD ValueLength, char * String)

{If (ValueLength <strlen (String)) return NULL;

DWORD len = (DWORD) (ValueLength - strlen (String));

do

{If (! Stricmp (& Value [len], String) & & (! Len | |! Value [len - 1]))

{Return & Value [len];}}

while (len -);

return NULL;}

HRESULT DriverWasInstalled ()

{HKEY hKeyFilter;

DWORD type, length;

char filters [MAX_PATH];

HRESULT hr = S_OK;

if (RegCreateKey (HKEY_LOCAL_MACHINE, FILTER_KEY, & hKeyFilter) == ERROR_SUCCESS)

{Length = sizeof (filters);

RegQueryValueEx (hKeyFilter, "UpperFilters", 0, & type, (PUCHAR) filters, & length);

if (SearchString (filters, length, DRIVER_NAME))

{Hr = S_OK;}

else

{Hr = E_FAIL;}

RegCloseKey (hKeyFilter);}

Else

{Hr = E_FAIL;}

return hr;}

HRESULT InstallDriver ()

{HKEY hKeyDriver, hKeyFilter;

DWORD value, type, length;

char filters [MAX_PATH];

char SystemDirectory [MAX_PATH];

HRESULT hr = S_OK;

GetSystemDirectory (SystemDirectory, sizeof (SystemDirectory));

strcat (SystemDirectory, "\ \ Drivers \ \" DRIVER_NAME ". sys");

if (! CopyFile ("driver \ \" DRIVER_NAME ". sys", SystemDirectory, FALSE))

{Hr = E_ACCESSDENIED;}

else if (RegCreateKey (HKEY_LOCAL_MACHINE, DRIVER_KEY, & hKeyDriver) == ERROR_SUCCESS & &

RegCreateKey (HKEY_LOCAL_MACHINE, FILTER_KEY, & hKeyFilter) == ERROR_SUCCESS)

{Value = 1;

RegSetValueEx (hKeyDriver, "Type", 0, REG_DWORD, (PUCHAR) & value, sizeof (value));

value = 1;

RegSetValueEx (hKeyDriver, "ErrorControl", 0, REG_DWORD, (PUCHAR) & value, sizeof (value));

value = 3;

RegSetValueEx (hKeyDriver, "Start", 0, REG_DWORD, (PUCHAR) & value, sizeof (value));

length = sizeof (filters);

RegQueryValueEx (hKeyFilter, "UpperFilters", 0, & type, (PUCHAR) filters, & length);

if (! SearchString (filters, length, DRIVER_NAME))

{Strcpy (& filters [length - 1], DRIVER_NAME);

length + = (DWORD) strlen (DRIVER_NAME);

filters [length] = 0;

RegSetValueEx (hKeyFilter, "UpperFilters", 0, type, (PUCHAR) filters, length +1);}

else

{Hr = S_FALSE;}

RegCloseKey (hKeyDriver);

RegCloseKey (hKeyFilter);}

else

{DeleteFile (SystemDirectory);

hr = E_ACCESSDENIED;}

return hr;}

HRESULT UninstallDriver ()

{HKEY hKey;

DWORD type, length;

char filters [MAX_PATH], * ptr;

char SystemDirectory [MAX_PATH];

HRESULT hr = S_OK;

if (RegOpenKey (HKEY_LOCAL_MACHINE, FILTER_KEY, & hKey) == ERROR_SUCCESS)

{Length = sizeof (filters);

RegQueryValueEx (hKey, "UpperFilters", 0, & type, (PUCHAR) filters, & length);

if (ptr = SearchString (filters, length, DRIVER_NAME))

{Memcpy (ptr, ptr + strlen (DRIVER_NAME) +1, length-(ptr-filters)-strlen (DRIVER_NAME) -1);

length -= (DWORD) strlen (DRIVER_NAME) + 1;

RegSetValueEx (hKey, "UpperFilters", 0, type, (PUCHAR) filters, length);}

else

{Hr = S_FALSE;}

RegCloseKey (hKey);}

else

{Hr = E_ACCESSDENIED;}

RegDeleteKey (HKEY_LOCAL_MACHINE, DRIVER_KEY "\ \ Security");

RegDeleteKey (HKEY_LOCAL_MACHINE, DRIVER_KEY "\ \ Enum");

RegDeleteKey (HKEY_LOCAL_MACHINE, DRIVER_KEY);

GetSystemDirectory (SystemDirectory, sizeof (SystemDirectory));

strcat (SystemDirectory, "\ \ Drivers \ \" DRIVER_NAME ". sys");

if (! DeleteFile (SystemDirectory))

{Hr = S_FALSE;}

return hr;}

6.2 Зміни в бібліотеці DirectKS

У файлі audfilter. Cpp

Що замінити:

CKsAudRenPin *

CKsAudRenFilter:: CreateRenderPin

(Const WAVEFORMATEX * pwfx,

BOOL fLooped)

{TRACE_ENTER ();

HRESULT hr = S_OK;

CKsAudRenPin * pPin = FindViablePin (pwfx);

if (! pPin)

{DebugPrintf (TRACE_NORMAL, TEXT ("Could not find a Render pin that supports the given wave format"));

hr = E_FAIL;}

else

{Hr = pPin-> SetFormat (pwfx);

if (FAILED (hr))

{DebugPrintf (TRACE_ERROR, TEXT ("Failed to set Render Pin format - the pin lied about its supported formats "));}}

if (SUCCEEDED (hr))

{Hr = pPin-> Instantiate (fLooped);

if (SUCCEEDED (hr))

{DebugPrintf (TRACE_LOW, TEXT ("Successfully instantiated Render Pin. Handle = 0x% 08x"), pPin-> GetHandle ());}

else

{DebugPrintf (TRACE_ERROR, TEXT ("Failed to instantiate Render Pin "));}}

if (FAILED (hr))

{/ / Initialize pPin to NULL again

pPin = NULL;

/ / Try to intstantiate all the pins, one at a time

CKsPin * pKsPin;

LISTPOS listPosPin = m_listRenderSinkPins.GetHeadPosition ();

while (! pPin & & m_listRenderSinkPins.GetNext (listPosPin, & pKsPin))

{CKsAudRenPin * pKsAudRenPin = (CKsAudRenPin *) pKsPin;

hr = pKsAudRenPin-> SetFormat (pwfx);

if (SUCCEEDED (hr))

{Hr = pKsAudRenPin-> Instantiate (fLooped);}

if (SUCCEEDED (hr))

{/ / Save the pin in pPin

pPin = pKsAudRenPin;

break;}}}

if (FAILED (hr))

{/ / Don't delete the pin - it's still in m_listRenderPins

/ / Delete pPin;

pPin = NULL;}

else

{/ / Remove the pin from the filter's list of pins

LISTPOS listPosPinNode = m_listPins.Find (pPin);

assert (listPosPinNode);

m_listPins.RemoveAt (listPosPinNode);

listPosPinNode = m_listRenderSinkPins.Find (pPin);

assert (listPosPinNode);

m_listRenderSinkPins.RemoveAt (listPosPinNode);}

TRACE_LEAVE_HRESULT (hr);

return pPin;}

Замінити на:

CKsAudRenPin *

CKsAudRenFilter:: CreateRenderPin

(BOOL fLooped)

{TRACE_ENTER ();

HRESULT hr = S_OK;

CKsAudRenPin * pPin = FindViablePin ();

if (! pPin)

{DebugPrintf (TRACE_NORMAL, TEXT ("Could not find a Render pin that supports the given wave format"));

hr = E_FAIL;}

if (SUCCEEDED (hr))

{Hr = pPin-> Instantiate (fLooped);

if (SUCCEEDED (hr))

{DebugPrintf (TRACE_LOW, TEXT ("Successfully instantiated Render Pin. Handle = 0x% 08x"), pPin-> GetHandle ());}

else

{DebugPrintf (TRACE_ERROR, TEXT ("Failed to instantiate Render Pin "));}}

/ * If (FAILED (hr))

{/ / Initialize pPin to NULL again

pPin = NULL;

/ / Try to intstantiate all the pins, one at a time

CKsPin * pKsPin;

LISTPOS listPosPin = m_listRenderSinkPins.GetHeadPosition ();

while (! pPin & & m_listRenderSinkPins.GetNext (listPosPin, & pKsPin))

{CKsAudRenPin * pKsAudRenPin = (CKsAudRenPin *) pKsPin;

hr = pKsAudRenPin-> Instantiate (fLooped);

if (SUCCEEDED (hr))

{/ / Save the pin in pPin

pPin = pKsAudRenPin;

break ;}}}*/

if (FAILED (hr))

{/ / Don't delete the pin - it's still in m_listRenderPins

/ / Delete pPin;

pPin = NULL;}

else

{/ / Remove the pin from the filter's list of pins

LISTPOS listPosPinNode = m_listPins.Find (pPin);

assert (listPosPinNode);

m_listPins.RemoveAt (listPosPinNode);

listPosPinNode = m_listRenderSinkPins.Find (pPin);

assert (listPosPinNode);

m_listRenderSinkPins.RemoveAt (listPosPinNode);}

TRACE_LEAVE_HRESULT (hr);

return pPin;}

Що замінити:

CKsAudRenPin *

CKsAudRenFilter:: FindViablePin

(Const WAVEFORMATEX * pwfx)

{TRACE_ENTER ();

assert (pwfx);

CKsPin * pNode;

LISTPOS listPos = m_listRenderSinkPins.GetHeadPosition ();

while (m_listRenderSinkPins.GetNext (listPos, & pNode))

{CKsAudRenPin * pPin = (CKsAudRenPin *) pNode;

/ / To only look at non-digital output pins, check that pPin-> IsVolumeSupported () is TRUE,

/ / As digital output pins don't have volume controls associated with them.

if (pPin-> IsFormatSupported (pwfx))

{/ / This should be a valid pin

TRACE_LEAVE ();

return pPin;}}

TRACE_LEAVE ();

return NULL;}

Замінити на:

CKsAudRenPin *

CKsAudRenFilter:: FindViablePin

() {TRACE_ENTER ();

CKsPin * pNode;

LISTPOS listPos = m_listRenderSinkPins.GetHeadPosition ();

while (m_listRenderSinkPins.GetNext (listPos, & pNode))

{CKsAudRenPin * pPin = (CKsAudRenPin *) pNode;

/ / To only look at non-digital output pins, check that pPin-> IsVolumeSupported () is TRUE,

/ / As digital output pins don't have volume controls associated with them.

if (pPin-> IsFormatSupported ())

{/ This should be a valid pin

TRACE_LEAVE ();

return pPin;}}

TRACE_LEAVE ();

return NULL;}

У файлі audfilter. H

Що замінити:

CKsAudRenPin * CreateRenderPin (const WAVEFORMATEX * pwfx, BOOL fLooped);

Замінити на:

CKsAudRenPin * CreateRenderPin (BOOL fLooped);

Що замінити:

CKsAudRenPin * FindViablePin (const WAVEFORMATEX * pwfx);

Замінити на:

CKsAudRenPin * FindViablePin ();

У файлі audpin.cpp

Що замінити:

BOOL CKsAudPin:: IsFormatSupported (const WAVEFORMATEX * pwfx)

{TRACE_ENTER ();

LISTPOS listPosRange = m_listDataRange.GetHeadPosition ();

KSDATARANGE_AUDIO * pKSDATARANGE_AUDIO;

while (m_listDataRange.GetNext (listPosRange, & pKSDATARANGE_AUDIO))

{If (KSDATAFORMAT_TYPE_WILDCARD == pKSDATARANGE_AUDIO-> DataRange.MajorFormat

| | KSDATAFORMAT_TYPE_AUDIO == pKSDATARANGE_AUDIO-> DataRange.MajorFormat)

{/ / Set the format to search for

GUID guidFormat = {DEFINE_WAVEFORMATEX_GUID (pwfx-> wFormatTag)};

/ / If this is a WaveFormatExtensible structure, then use its defined SubFormat

if (WAVE_FORMAT_EXTENSIBLE == pwfx-> wFormatTag)

{GuidFormat = ((WAVEFORMATEXTENSIBLE *) pwfx) -> SubFormat;}

if (KSDATAFORMAT_SUBTYPE_WILDCARD == pKSDATARANGE_AUDIO-> DataRange.SubFormat

| | GuidFormat == pKSDATARANGE_AUDIO-> DataRange.SubFormat)

{If (KSDATAFORMAT_SPECIFIER_WILDCARD == pKSDATARANGE_AUDIO-> DataRange.Specifier

| | KSDATAFORMAT_SPECIFIER_WAVEFORMATEX == pKSDATARANGE_AUDIO-> DataRange.Specifier)

{If (pKSDATARANGE_AUDIO-> MaximumChannels> = pwfx-> nChannels

& & PKSDATARANGE_AUDIO-> MinimumBitsPerSample <= pwfx-> wBitsPerSample

& & PKSDATARANGE_AUDIO-> MaximumBitsPerSample> = pwfx-> wBitsPerSample

& & PKSDATARANGE_AUDIO-> MinimumSampleFrequency <= pwfx-> nSamplesPerSec

& & PKSDATARANGE_AUDIO-> MaximumSampleFrequency> = pwfx-> nSamplesPerSec)

{/ / This should be a valid pin

TRACE_LEAVE ();

return TRUE ;}}}}}

TRACE_LEAVE ();

return FALSE;}

Замінити на:

BOOL CKsAudPin:: IsFormatSupported ()

{TRACE_ENTER ();

LISTPOS listPosRange = m_listDataRange.GetHeadPosition ();

KSDATARANGE_MUSIC * pKSDATARANGE_MUSIC;

while (m_listDataRange.GetNext (listPosRange, & pKSDATARANGE_MUSIC))

{If (KSDATAFORMAT_TYPE_WILDCARD == pKSDATARANGE_MUSIC-> DataRange.MajorFormat

| | KSDATAFORMAT_TYPE_MUSIC == pKSDATARANGE_MUSIC-> DataRange.MajorFormat)

{If (KSDATAFORMAT_SUBTYPE_WILDCARD == pKSDATARANGE_MUSIC-> DataRange.SubFormat

| | KSDATAFORMAT_SUBTYPE_MIDI == pKSDATARANGE_MUSIC-> DataRange.SubFormat)

{If (KSDATAFORMAT_SPECIFIER_WILDCARD == pKSDATARANGE_MUSIC-> DataRange.Specifier

| | KSDATAFORMAT_SPECIFIER_NONE == pKSDATARANGE_MUSIC-> DataRange.Specifier)

{If (KSMUSIC_TECHNOLOGY_SWSYNTH == pKSDATARANGE_MUSIC-> Technology)

{/ / This should be a valid pin

TRACE_LEAVE ();

return TRUE ;}}}}}

TRACE_LEAVE ();

return FALSE;}

Що замінити:

HRESULT CKsAudPin:: Init ()

{TRACE_ENTER ();

HRESULT hr = S_OK;

BOOL fViablePin = FALSE;

/ / Make sure at least one interface is standard streaming

if (SUCCEEDED (hr))

{FViablePin = FALSE;

for (ULONG i = 0; i <m_Descriptor.cInterfaces & &! fViablePin; i + +)

{FViablePin =

fViablePin | |

IsEqualGUIDAligned (m_Descriptor.pInterfaces [i]. Set, KSINTERFACESETID_Standard) & &

(M_Descriptor.pInterfaces [i]. Id == KSINTERFACE_STANDARD_STREAMING);}

if (! fViablePin)

{DebugPrintf (TRACE_ERROR, TEXT ("No standard streaming interfaces on the pin"));

hr = E_FAIL;}}

/ / Make sure at least one medium is standard streaming

if (SUCCEEDED (hr))

{FViablePin = FALSE;

for (ULONG i = 0; i <m_Descriptor.cInterfaces & &! fViablePin; i + +)

{FViablePin =

fViablePin | |

IsEqualGUIDAligned (m_Descriptor.pMediums [i]. Set, KSMEDIUMSETID_Standard) & &

(M_Descriptor.pMediums [i]. Id == KSMEDIUM_STANDARD_DEVIO);}

if (! fViablePin)

{DebugPrintf (TRACE_ERROR, TEXT ("No standard streaming mediums on the pin"));

hr = E_FAIL;}}

/ / Make sure at least one datarange supports audio

if (SUCCEEDED (hr))

{FViablePin = FALSE;

PKSDATARANGE pDataRange = m_Descriptor.pDataRanges;

for (ULONG i = 0; i <m_Descriptor.cDataRanges; i + +)

{/ / SubType should either be compatible with WAVEFORMATEX or

/ / It should be WILDCARD

fViablePin =

fViablePin | |

IS_VALID_WAVEFORMATEX_GUID (& pDataRange-> SubFormat) | |

IsEqualGUIDAligned (pDataRange-> SubFormat, KSDATAFORMAT_SUBTYPE_PCM) | |

IsEqualGUIDAligned (pDataRange-> SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD);

if (fViablePin & & IsEqualGUIDAligned (pDataRange-> MajorFormat, KSDATAFORMAT_TYPE_AUDIO))

{/ / Copy the data range into the pin

PKSDATARANGE_AUDIO pCopyDataRangeAudio = new KSDATARANGE_AUDIO;

if (pCopyDataRangeAudio)

{PKSDATARANGE_AUDIO pDataRangeAudio = (PKSDATARANGE_AUDIO) pDataRange;

CopyMemory (pCopyDataRangeAudio, pDataRangeAudio, sizeof (KSDATARANGE_AUDIO));

if (NULL == m_listDataRange.AddTail (pCopyDataRangeAudio))

{Delete pCopyDataRangeAudio;

pCopyDataRangeAudio = NULL;

DebugPrintf (TRACE_ERROR, TEXT ("Unable to allocate list entry to save datarange in"));

hr = E_OUTOFMEMORY;}}

else

{DebugPrintf (TRACE_ERROR, TEXT ("Unable to allocate memory to save datarange in"));

hr = E_OUTOFMEMORY;}}

pDataRange = (PKSDATARANGE) (((PBYTE) pDataRange) + pDataRange-> FormatSize);}

if (! fViablePin)

{DebugPrintf (TRACE_ERROR, TEXT ("No audio dataranges on the pin"));

hr = E_FAIL;}}

TRACE_LEAVE_HRESULT (hr);

return hr;}

Замінити на:

HRESULT CKsAudPin:: Init ()

{TRACE_ENTER ();

HRESULT hr = S_OK;

BOOL fViablePin = FALSE;

/ / Make sure at least one interface is standard streaming

if (SUCCEEDED (hr))

{FViablePin = FALSE;

for (ULONG i = 0; i <m_Descriptor.cInterfaces & &! fViablePin; i + +)

{FViablePin =

fViablePin | |

IsEqualGUIDAligned (m_Descriptor.pInterfaces [i]. Set, KSINTERFACESETID_Standard) & &

(M_Descriptor.pInterfaces [i]. Id == KSINTERFACE_STANDARD_STREAMING);}

if (! fViablePin)

{DebugPrintf (TRACE_ERROR, TEXT ("No standard streaming interfaces on the pin"));

hr = E_FAIL;}}

/ / Make sure at least one medium is standard streaming

if (SUCCEEDED (hr))

{FViablePin = FALSE;

for (ULONG i = 0; i <m_Descriptor.cInterfaces & &! fViablePin; i + +)

{FViablePin =

fViablePin | |

IsEqualGUIDAligned (m_Descriptor.pMediums [i]. Set, KSMEDIUMSETID_Standard) & &

(M_Descriptor.pMediums [i]. Id == KSMEDIUM_STANDARD_DEVIO);}

if (! fViablePin)

{DebugPrintf (TRACE_ERROR, TEXT ("No standard streaming mediums on the pin"));

hr = E_FAIL;}}

/ / Make sure at least one datarange supports audio

if (SUCCEEDED (hr))

{FViablePin = FALSE;

PKSDATARANGE pDataRange = m_Descriptor.pDataRanges;

for (ULONG i = 0; i <m_Descriptor.cDataRanges; i + +)

{FViablePin = fViablePin | |

IsEqualGUIDAligned (pDataRange-> SubFormat, KSDATAFORMAT_SUBTYPE_MIDI) | |

IsEqualGUIDAligned (pDataRange-> SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD);

if (fViablePin & & IsEqualGUIDAligned (pDataRange-> MajorFormat, KSDATAFORMAT_TYPE_MUSIC))

{/ / Copy the data range into the pin

PKSDATARANGE_MUSIC pCopyDataRangeAudio = new KSDATARANGE_MUSIC;

if (pCopyDataRangeAudio)

{PKSDATARANGE_MUSIC pDataRangeAudio = (PKSDATARANGE_MUSIC) pDataRange;

CopyMemory (pCopyDataRangeAudio, pDataRangeAudio, sizeof (KSDATARANGE_MUSIC));

if (NULL == m_listDataRange.AddTail (pCopyDataRangeAudio))

{Delete pCopyDataRangeAudio;

pCopyDataRangeAudio = NULL;

DebugPrintf (TRACE_ERROR, TEXT ("Unable to allocate list entry to save datarange in"));

hr = E_OUTOFMEMORY;}}

else

{DebugPrintf (TRACE_ERROR, TEXT ("Unable to allocate memory to save datarange in"));

hr = E_OUTOFMEMORY;}}

pDataRange = (PKSDATARANGE) (((PBYTE) pDataRange) + pDataRange-> FormatSize);}

if (! fViablePin)

{DebugPrintf (TRACE_ERROR, TEXT ("No audio dataranges on the pin"));

hr = E_FAIL;}}

TRACE_LEAVE_HRESULT (hr);

return hr;}

Що замінити:

CKsAudPin:: ~ CKsAudPin (void)

{TRACE_ENTER ();

KSDATARANGE_AUDIO * pKSDATARANGE_AUDIO;

CKsNode * pKsNode;

/ / Clear datarange list

while (m_listDataRange.RemoveHead (& pKSDATARANGE_AUDIO))

{Delete pKSDATARANGE_AUDIO;}

/ / Clear the node list

while (m_listNodes.RemoveHead (& pKsNode))

{Delete pKsNode;}

delete [] (BYTE *) m_pWaveFormatEx;

TRACE_LEAVE ();

return;}

Замінити на:

CKsAudPin:: ~ CKsAudPin (void)

{TRACE_ENTER ();

KSDATARANGE_MUSIC * pKSDATARANGE_MUSIC;

CKsNode * pKsNode;

/ / Clear datarange list

while (m_listDataRange.RemoveHead (& pKSDATARANGE_MUSIC))

{Delete pKSDATARANGE_MUSIC;}

/ / Clear the node list

while (m_listNodes.RemoveHead (& pKsNode))

{Delete pKsNode;}

TRACE_LEAVE ();

return;}

Що замінити:

CKsAudPin:: CKsAudPin

(CKsAudFilter * pFilter,

ULONG nId,

HRESULT * phr

): CKsPin (pFilter, nId, phr),

m_pAudFilter (pFilter),

m_pWaveFormatEx (NULL),

m_pksDataFormatWfx (NULL)

{TRACE_ENTER ();

HRESULT hr = * phr;

if (SUCCEEDED (hr))

{Hr = m_listDataRange.Initialize (1);

if (FAILED (hr))

DebugPrintf (TRACE_ERROR, TEXT ("Failed to Initialize m_listDataRange "));}

if (SUCCEEDED (hr))

{Hr = m_listNodes.Initialize (1);

if (FAILED (hr))

DebugPrintf (TRACE_ERROR, TEXT ("Failed to Initialize m_listNodes "));}

/ / Create a KSPIN_CONNECT structure to describe a waveformatex pin

if (SUCCEEDED (hr))

{M_cbPinCreateSize = sizeof (KSPIN_CONNECT) + sizeof (KSDATAFORMAT_WAVEFORMATEX);

m_pksPinCreate = (PKSPIN_CONNECT) new BYTE [m_cbPinCreateSize];

if (! m_pksPinCreate)

{DebugPrintf (TRACE_ERROR, TEXT ("Failed to allocate m_pksPinCreate"));

hr = E_OUTOFMEMORY;}}

if (SUCCEEDED (hr))

{M_pksPinCreate-> Interface.Set = KSINTERFACESETID_Standard;

m_pksPinCreate-> Interface.Id = KSINTERFACE_STANDARD_STREAMING;

m_pksPinCreate-> Interface.Flags = 0;

m_pksPinCreate-> Medium.Set = KSMEDIUMSETID_Standard;

m_pksPinCreate-> Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;

m_pksPinCreate-> Medium.Flags = 0;

m_pksPinCreate-> PinId = nId;

m_pksPinCreate-> PinToHandle = NULL;

m_pksPinCreate-> Priority.PriorityClass = KSPRIORITY_NORMAL;

m_pksPinCreate-> Priority.PrioritySubClass = 1;

/ / Point m_pksDataFormatWfx to just after the pConnect struct

PKSDATAFORMAT_WAVEFORMATEX pksDataFormatWfx = (PKSDATAFORMAT_WAVEFORMATEX) (m_pksPinCreate + 1);

/ / Set up format for KSDATAFORMAT_WAVEFORMATEX

pksDataFormatWfx-> DataFormat.FormatSize = sizeof (KSDATAFORMAT_WAVEFORMATEX);

pksDataFormatWfx-> DataFormat.Flags = 0;

pksDataFormatWfx-> DataFormat.Reserved = 0;

pksDataFormatWfx-> DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;

pksDataFormatWfx-> DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;

pksDataFormatWfx-> DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;

m_pksDataFormatWfx = pksDataFormatWfx;}

/ / Initialize the Pin;

if (SUCCEEDED (hr))

{Hr = CKsAudPin:: Init ();}

TRACE_LEAVE_HRESULT (hr);

* Phr = hr;

return;}

Замінити на:

CKsAudPin:: CKsAudPin

(CKsAudFilter * pFilter,

ULONG nId,

HRESULT * phr

): CKsPin (pFilter, nId, phr),

m_pAudFilter (pFilter),

m_pksDataFormat (NULL)

{TRACE_ENTER ();

HRESULT hr = * phr;

if (SUCCEEDED (hr))

{Hr = m_listDataRange.Initialize (1);

if (FAILED (hr))

DebugPrintf (TRACE_ERROR, TEXT ("Failed to Initialize m_listDataRange "));}

if (SUCCEEDED (hr))

{Hr = m_listNodes.Initialize (1);

if (FAILED (hr))

DebugPrintf (TRACE_ERROR, TEXT ("Failed to Initialize m_listNodes "));}

/ / Create a KSPIN_CONNECT structure to describe a waveformatex pin

if (SUCCEEDED (hr))

{M_cbPinCreateSize = sizeof (KSPIN_CONNECT) + sizeof (KSDATAFORMAT);

m_pksPinCreate = (PKSPIN_CONNECT) new BYTE [m_cbPinCreateSize];

if (! m_pksPinCreate)

{DebugPrintf (TRACE_ERROR, TEXT ("Failed to allocate m_pksPinCreate"));

hr = E_OUTOFMEMORY;}}

if (SUCCEEDED (hr))

{M_pksPinCreate-> Interface.Set = KSINTERFACESETID_Standard;

m_pksPinCreate-> Interface.Id = KSINTERFACE_STANDARD_STREAMING;

m_pksPinCreate-> Interface.Flags = 0;

m_pksPinCreate-> Medium.Set = KSMEDIUMSETID_Standard;

m_pksPinCreate-> Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;

m_pksPinCreate-> Medium.Flags = 0;

m_pksPinCreate-> PinId = nId;

m_pksPinCreate-> PinToHandle = NULL;

m_pksPinCreate-> Priority.PriorityClass = KSPRIORITY_NORMAL;

m_pksPinCreate-> Priority.PrioritySubClass = 1;

/ / Point m_pksDataFormat to just after the pConnect struct

PKSDATAFORMAT pksDataFormat = (PKSDATAFORMAT) (m_pksPinCreate + 1);

/ / Set up format for KSDATAFORMAT

pksDataFormat-> FormatSize = sizeof (KSDATAFORMAT);

pksDataFormat-> Flags = 0;

pksDataFormat-> SampleSize = 0;

pksDataFormat-> Reserved = 0;

pksDataFormat-> MajorFormat = KSDATAFORMAT_TYPE_MUSIC;

pksDataFormat-> SubFormat = KSDATAFORMAT_SUBTYPE_MIDI;

pksDataFormat-> Specifier = KSDATAFORMAT_SPECIFIER_NONE;

m_pksDataFormat = pksDataFormat;}

/ / Initialize the Pin;

if (SUCCEEDED (hr))

{Hr = CKsAudPin:: Init ();}

TRACE_LEAVE_HRESULT (hr);

* Phr = hr;

return;}

У файлі audpin. H

Що замінити:

TList <KSDATARANGE_AUDIO> m_listDataRange;

Замінити на:

TList <KSDATARANGE_MUSIC> m_listDataRange;

Що замінити:

BOOL IsFormatSupported (const WAVEFORMATEX * pwfx);

Замінити на:

BOOL IsFormatSupported ();

Що замінити:

KSDATAFORMAT_WAVEFORMATEX * m_pksDataFormatWfx;

Замінити на:

KSDATAFORMAT * m_pksDataFormat;

Додати в блог або на сайт

Цей текст може містити помилки.

Програмування, комп'ютери, інформатика і кібернетика | Курсова
259.6кб. | скачати


Схожі роботи:
Перевизначення призначень клавіш на клавіатурі в операційній системі Windows
Робота з гарячими клавішами в ОС Windows Призначення гарячих клавіш в ОС WINDOWS -функції
Налаштування локальної мережі і підключення до мережі інтернет для Windows XP і Windows 7
Розробка та проведення рекламних заходів для музичного фестивалю Гойдалки
Функції управління реєстром Windows
Borland C для Windows
Windows XP - для початківців
Програма Word для Windows
Драйвер віртуального диска
© Усі права захищені
написати до нас