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

Здравствуйте, джентльмены!

Сегодня, работая над проектом не за своей обычной машиной, а за подвернувшимся под руку Windows-сервером, я случайно узнал о любопытном но, к счастью, документированном нюансе класса Sound. Если в системе отсутствует звуковая карта, то после попытки воспроизведения звука метод play() возвращает null, что может стать причиной ошибки, генерируемой отлично работавшим на других машинах кодом.

Приведу пример:

var menuMusic: Sound = new MenuMusic; // так называется наш звук
var menuMusicSoundChannel: SoundChannel = new SoundChannel(); // здесь пока всё в порядке
menuMusicSoundChannel = menuMusic.play(); // строка выполняется, но после её выполнения menuMusicSoundChannel будет равно null
menuMusicSoundChannel.stop(); // и вот здесь флэшплеер выдаст ошибку об обращении к методу объекта, являющегося null'ом.

Указанный выше код отлично работает на машине со звуковой картой, но поведёт себя так, как написано в комментариях, на системе без неё.

Чтобы избежать подобной ситуации, я предлагаю добавлять при запуске приложения небольшую проверку:

var menuMusic: Sound = new MenuMusic; // какой-нибудь тестовый звук
var menuMusicSoundChannel: SoundChannel = new SoundChannel();
menuMusicSoundChannel = menuMusic.play(); // делаем тестовый запуск звука
if (menuMusicSoundChannel)
{
  trace("Звук есть!");
  menuMusicSoundChannel.stop(); // всё, проверку мы прошли, тестовый звук можно тормозить
}
else
{
  trace("Грусьть и пичяль, звука нет"); // звука нет, потому больше аудио воспроизводить не пытаемся
}


Я согласен, что сейчас практически не встретишь устройство без хоть какого-нибудь звука, однако, думаю, включить этот нюанс в чек-лист не помешает.

Спасибо за внимание.
  • +17

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

0
Не знал, спасибо.
+4
По этому поводу у Шпилей есть даже специальный документ в помощь разработчикам:
To make sure your game meets this requirement:
Having unplugged speakers or headset in windows 7 doesn’t cause a fatal error.
Before doing something with a sound channel first see if it exists.
In win 7 when you unplug your headphone the sound.play(..) will return null.
If not checked upon this might result in an error
To fix this use this code:
var sound:Sound = new Sound();
var channel:SoundChannel = sound.play(0, loops);
if (!channel) return null;
channel.
0
Прикольно))) Даже и не думал
+1
Да, у меня Спиловские тестеры выловили этот баг и прислали док о том, как это дело пофиксить. :)
0
Такому штату тестеров, любой девелопер позавидует, если они умудряются даже такие баги отловить…

На днях прочел постмортем «Мясного пацана», тоже удивил момент, когда программист каждый новый рабочий день, начинал с исправления ошибок. Даже боюсь представить, что у меня в коде творится))
+1
А где можно увидеть полную версию документа?
0
Присоединяюсь к вопросу :-)
0
Мне высылали только часть, касающуюся этой проблемы со звуком.
0
Аналогично, выслали ворд-документ который я полностью скопировал выше.
Возможно таких документов с «частыми ошибками» много, было бы здорово раздобыть все
+1
Еще null вернется, если одновременно проигрывается 32 звука, т.е все каналы заняты.
+1
Спасибо!

Огласите пожалуйста весь чек-лист.
+2
1. Написать игру
2. Проверить звук
3. ????????
4. PROFIT
+3
Да, это грабля начинающего. Мне заранее подсказали. Однако, вообще говоря описанная проверка не нужна. Ибо нужно писать код sound manager так, чтобы всегда проверять channel на null после play(). Ибо null может вернуться не только при отсутствии звуковой карты, но и при превышении количества используемых каналов (32 max) и что самое неприятное — при каких-то других проблемах. В итоге, если не учитывать это — игра может сыпаться в самых неожиданных и неповторяемых местах.
Как пример — здесь на блогах я давал пример моего класса SoundManager. Там все написано безопасно. Хотя я умудрился ровно в одном месте заложиться на то, что null не вернется. И получил редкую багу. Повторить я ее не мог. Багрепорты Конга малоосмысленные были. Случайно на первой неделе дистрибуции я дебажил ради других целей и словил багу. Оказалось, что я заложился на то, что если я при паузе все звуки глушу, то при unpause, восстанавливая эти звуки я никак не могу получить null, ибо если было меньше 32 канала до паузы — то при анпаузе никак не может быть больше 32-ух. Однако, по непонятно причине происходило падение.
0
Проще ловить исключения, а не гадать проявится / не проявится.
0
Не понял логику. Ты предлагаешь вместо проверки на null после play сразу работать с channel и оборачивать это все в трайкетч «а упадет ли если мы дернем метод у null»?

Трайкетчи надо ставить там, где в доках прописано что эксепшн выкинут определенного типа. Например сетевые штуки обязательно. Наугад заворачивать все в трайкетчи можно только если не можешь поймать, а у тебя игра в релизе и надо срочно. Ты же не будешь всю игру заворачивать на всякий в трайкетч? Я же рассказываю что null вернуло в валидной по докам ситуации.
Логику что кончились каналы надо решать сперва самостоятельно, распределив все звуки по группам и самому держать их в пределах. На крайняк все равно проверять на null и валидно это отрабатывать. Трай кетчами это тоже можно сделать но это будет медленно (и неверно).
0
Исключения работают быстро, если они не выбрасываются. Т.е. просто оборачивание в try/catch не приводит к потере производительности.

У меня работает как-то так.
public function play(name: String): void
{
try
{
// тут по любому где нибудь будет getDefinitionByName,
// поэтому исключения ловить нужно, а заодно ловим редкие баги с
// нулевым SoundChannel.
}
catch (e: Error)
{
}
}
0
Ну у меня нет getDefByName
И все равно выглядит криво. Ну то-есть это надо еще подумать как сделать так, чтобы после слёта через эксепш все продолжало работать нормально! Вот если оборвать саунд менеджер где-то посередине из-за того что null вывалился, то нарушатся стройность данных в структурах. Я не уверен что это выльется просто в один не прозвучавший звук. Если логика в саунд менеджере чуть более сложная чем просто «всем звучать!», то лучше продумать все и ловить самому все эти null-ы и валидно в структуре отражать. Глянь мой саунд менеджер, я здесь выкладывал. Там без логики не обойтись, иначе какофония полная будет. Звук колонн подорогам надо слеплять вместе итд. Если что-то посередине оборвать — больше шансов что начнет валиться из-за неконсистентности. Лучше валидно отлавливать все возможные null.
0
А поверх обернуть try/catch :D.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.