Мультимедиа. Работа со звуком
Обратите внимание, что при завершении работы нашего приложения необходимо закрыть аудио-устройство и удалить вызов аудио с помощью методов void AudioDeviceManager::closeAudioDevice() и void AudioDeviceManager::removeAudioCallback(AudioIODeviceCallback* callback). Кроме того, необходимо обнулить объявленные указатели ( пример 19.3).
TCentralComponent::~TCentralComponent() { pAudioDeviceManager->closeAudioDevice(); TransportSource.setSource(0); AudioPlayer.setSource(0); pAudioDeviceManager->removeAudioCallback(& AudioPlayer); if(pAudioDeviceManager) delete pAudioDeviceManager; if(pCurrentAudioFileSource) delete pCurrentAudioFileSource; if(pFileNameDialog) delete pFileNameDialog; deleteAllChildren(); }Листинг 19.3. Реализация деструктора класса компонента содержимого TCentralComponent (файл TCentralComponent.cpp)
Наша программа будет менять цвет и текст кнопки pPlayButton в зависимости от того, идёт ли воспроизведение аудио-файла, или оно остановлено ( пример 19.4). Узнать, осуществляется ли воспроизведение в текущий момент, позводяет метод bool AudioTransportSource::isPlaying() const throw().
void TCentralComponent::changeListenerCallback(ChangeBroadcaster*) { // Если идёт воспроизведение... if(TransportSource.isPlaying()) { pPlayButton->setButtonText(tr("Остановить")); // изменяем цвет кнопки на розовый pPlayButton->setColour(TextButton::buttonColourId, Colours::pink); } else { pPlayButton->setButtonText(tr("Играть")); // иначе - меняем на зелёный. pPlayButton->setColour(TextButton::buttonColourId, Colours::lightgreen); } }Листинг 19.4. Реализация метода changeListenerCallback класса компонента содержимого TCentralComponent (файл TCentralComponent.cpp)
Управление воспроизведением осуществляется в обработчике нажатия на кнопку pPlayButton ( пример 19.5). В нём мы используем два метода класса AudioTransportSource:
- void AudioTransportSource::start() — начинает воспроизведение, если его источник был выбран.
- void AudioTransportSource::stop() — останавливает воспроизведение, посылая при этом сигнал слушателю изменений (ChangeListener), зарегистрированному для текщего объекта класса AudioTransportSource.
Перед началом воспроизведения необходимо установить его позицию в текущем аудио-потоке методом void AudioTransportSource::setPosition(double newPosition). В нашем примере воспроизведение при нажатии на кнопку pPlayButton каждый раз начинается заново ( пример 19.5), однако, если сохранять текущую позицию воспроизведения, получаемую методом double AudioTransportSource::getCurrentPosition() const, в какой-либо переменной и начинать в последующем проигрывание именно с этой позиции, то можно реализовать функцию паузы. Возвращаемое методом setPosition значение представляет собой время в секундах.
else if(pButton == pPlayButton) { // Если идёт воспроизведение... if(TransportSource.isPlaying()) { // останавливаем его TransportSource.stop(); } else { // Иначе - устанавливаем позицию в начало потока воспроизведения... TransportSource.setPosition(0); // и начинаем проигрывание файла TransportSource.start(); } }Листинг 19.5. Реализация части метода buttonClicked класса компонента содержимого TCentralComponent (файл TCentralComponent.cpp)
Загрузка аудио-файла в поток, и подготовка его к воспроизведению реализована в обработчике нажатия на кнопку — pChooseButton ( пример 19.6). Выше мы уже описали взаимодействие используемых в нём классов.
void TCentralComponent::buttonClicked(Button* pButton) { if(pButton == pChooseButton) { // Создаём "обёртку" для показа диалога... DialogWindow::showModalDialog(tr("Выбор файла"), pFileNameDialog, NULL, Colours::azure, true, false, false); // Когда файл открыт... CurrentFile = pFileNameDialog->getCurrentFile(); // проверяем, существует ли он if(!(CurrentFile == File::nonexistent)) { // Выводим на ярлык название файла pNameLabel->setText(CurrentFile.getFileName(), false); // Если ранее осуществлялось воспроизведение - останавливаем его. TransportSource.stop(); TransportSource.setSource(0); pCurrentAudioFileSource = 0; AudioFormatManager FormatManager; // Загрузка файла в поток AudioFormatReader* pAudioFormatReader; // Регистрируем базовые форматы аудио FormatManager.registerBasicFormats(); / Создаём чтеца для текущего аудио-файла pAudioFormatReader = FormatManager.createReaderFor(CurrentFile); if(pAudioFormatReader != 0) { // Если чтец существует, // создаём с его помощью новый источник аудио-данных pCurrentAudioFileSource = new AudioFormatReaderSource(pAudioFormatReader, true); // и передаём их в AudioTransportSource TransportSource.setSource( pCurrentAudioFileSource, 32768, pAudioFormatReader->sampleRate); if(pCurrentAudioFileSource == 0) { AlertWindow::showMessageBox( AlertWindow::WarningIcon, tr("Ошибка воспроизведения"), tr("Отсутствует источник воспроизведения аудио-файла."), tr("Принять"), 0); return; } } else AlertWindow::showMessageBox( AlertWindow::WarningIcon, tr("Ошибка воспроизведения"), tr("Не инициирован источник чтения аудио-файла.\n Возможно, файл повреждён."), tr("Принять"), 0 ); } else { pNameLabel->setText(tr("Файл не выбран!"), false); } } // ...Листинг 19.6. Реализация части метода buttonClicked класса компонента содержимого TCentralComponent (файл TCentralComponent.cpp)
Понятно, что для того, чтобы программа, написанная с помощью Juce, могла воспроизводить тот или иной формат аудио, необходимо написать класс, унаследованный от AudioFormat, в котором будет осуществляться чтение и декодирование аудио-данных. В последующем новый аудио-формат должен быть зарегистрирован в AudioFormatManager с помощью метода void AudioFormatManager::registerFormat(AudioFormat* newFormat, bool makeThisTheDefaultFormat). В случае если мы хотим использовать новый формат по умолчанию, параметр makeThisTheDefaultFormat принимает значение true. Как упоминалось, в нашем примере мы зарегистрировали базовые аудио-форматы с помощью метода void AudioFormatManager::registerBasicFormats().
Членами сообщества уже созданы классы аудио-форматов, связывающие Juce с рядом свободных кодеков (например, avformat), что превращает её практически в универсальную мультимедийную библиотеку.