Audio recorder android studio

Работа с потоковым аудио

Введение

Конструктор

Для создания объекта нужно указать:

audioSource Откуда ведётся запись. В нашем случае это MediaRecorder.AudioSource.MIC
sampleRateInHz Частота дискретизации в герцах. Документация утверждает, что 44100Гц поддерживается всеми устройствами
channelConfig Конфигурация каналов. Может быть CHANNEL_IN_MONO или CHANNEL_IN_STEREO . Моно работает везде.

Важно: эти константы не совпадают с количеством каналов, которое они обозначают. В этот параметр нельзя передавать 1 или 2.

audioFormat Формат входных данных, более известный как кодек. Может быть ENCODING_PCM_16BIT или ENCODING_PCM_8BIT
bufferSizeInBytes Размер того самого внутреннего буфера. Из него можно считывать аудиопоток. Размер порции считывания не должен превышать эту величину. У этого параметра есть минимально допустимое значение, которое можно получить через getMinBufferSize() .

При своём создании объект пытается получить нужные ему ресурсы системы. Насколько удачно у него это получилось, можно узнать, вызвав функцию getState() . Если она вернёт STATE_INITIALIZED , то всё нормально, если STATE_UNINITIALIZED — значит, произошла ошибка.

Причин ошибки может быть две: слишком маленький буфер и недопустимый формат. Первого нужно избегать вызовом getMinBufferSize() . Второго, на самом деле, им же.

getMinBufferSize()

Этот статический метод выдаёт минимальный размер внутреннего буфера, при котором объект AudioRecord сможет работать. Параметры имеют тот же смысл, что и для конструктора. Следует заметить, что использование именно этой величины для записи — не лучшая идея. Если система будет ещё чем-то занята, то программа всё равно может не успевать считывать все данные подряд, и в записи будут дырки. Мне встречался совет брать размер в десять раз больше.

Получение списка форматов

Метод getMinBufferSize() имеет приятную особенность — ругаться на недопустимые для данного устройства параметры, возвращая ERROR_BAD_VALUE или ERROR . Это означает, что перебирая все возможные сочетания, можно узнать, какие форматы поддерживает устройство.

Считывание данных

Для получения данных из внутреннего буфера служит метод read() . Он существует в трёх вариантах:

  • read(byte[] audioData, int offsetInBytes, int sizeInBytes)
  • read(short[] audioData, int offsetInShorts, int sizeInShorts)
  • read(ByteBuffer audioBuffer, int sizeInBytes)

Их параметры:

audioData массив, в который будут записаны данные
audioBuffer буфер, в который будут записаны данные
offsetInBytes /
offsetInShorts
индекс, с которого начнётся запись
sizeInShorts размер запрашиваемого блока данных. В байтах для ByteBuffer и byte[] , в коротких целых для short[]

Если всё нормально, то метод вернёт количество прочитанных байт, если это вариант с ByteBuffer или byte[] , или прочитанных коротких целых для short[] . Если на момент вызова объект не был правильно инициализирован, то выдаст ERROR_INVALID_OPERATION, а если что-то не так с параметрами — ERROR_BAD_VALUE

Важно: метод блокирует вызывающий поток до тех пор, пока не считает запрошенное количество данных. Если во внутреннем буфере их недостаточно, то read() будет ожидать, пока они придут от микрофона. Поэтому метод следует вызывать из отдельного потока, иначе приложение будет висеть.

Подход, отход, фиксация

Чтобы программа могла получать данные от микрофона, нужно указать в файле AndroidManifest,xml соответствующее разрешение:

Чтобы начать запись, нужно вызвать метод startRecording() , а чтобы закончить — stop() . Запускать и останавливать запись можно сколько угодно раз.

После того, как работа с объектом закончена, следует вызвать метод release() . Он освободит все системные ресурсы, захваченные объектом. После этого объект нельзя использовать, а ссылающуюся на него переменную следует установить в null .

Важно: эти три метода, в отличие от упоминавшихся ранее, выбросят IllegalStateException , если их вызвать для неинициализированного (ну и слово. ) объекта или не в том порядке. Поэтому обращаться с ними нужно «аккуратно», т.е. через блок try .

Пример использования

Приведённый ниже класс делает всё то, о чём сказано выше. Кроме того, он посылает зарегистрированным у него Handler -ам сообщения о принятых данных. Эти данные будут обрабатываться в другом потоке, поэтому, чтобы не затереть ещё не обработанные данные новыми, использован циклический буфер.

В коде использован класс AudioFormatInfo . Он представляет собой POJO с тремя полями, описывающими формат записи: sampleRateInHz , channelConfig и audioFormat .

Источник

Создание собственного Android-диктофона с помощью Kotlin

Мультимедийный фреймворк Android поддерживает запись и воспроизведение аудио. В этой статье я покажу, как разработать простое приложение для звукозаписи, которое будет записывать аудио и сохранять его в локальном хранилище Android-устройства с помощью MediaRecorder из Android SDK.

Вы также узнаете, как запросить разрешения у пользователя в режиме реального времени и как работать с локальным хранилищем Android-устройства.

Создание пользовательского интерфейса

Сперва нам нужно создать интерфейс для звукозаписи. Это простой layout с тремя кнопками, которые будут использоваться для запуска, приостановки/возобновления и остановки записи.

Запрос требуемых разрешений

После создания пользовательского интерфейса мы можем начать использовать MediaRecorder для реализации основной функциональности нашего приложения. Но сначала нам нужно запросить необходимые разрешения для записи аудио и доступа к локальному хранилищу. Cделаем мы это это с помощью нескольких простых строк кода в нашем файле AndroidManifest.xml :

Также нужно проверить, одобрил ли пользователь разрешения, прежде чем мы сможем использовать наш MediaRecorder . Сделаем это в Activity MainActivity.kt :

Примечание: позже эти строки кода будут перемещены в OnClickListener кнопки начала записи аудио, чтобы мы могли убедиться, что MediaRecorder не будет запущен без необходимых разрешений.

Запись и сохранение аудио

Добавление OnClickListeners

Добавим слушатели к кнопкам, чтобы они реагировали на пользовательские события. Как я упоминал ранее, проверка на наличие необходимых разрешений будет добавлена в OnClickListener кнопки начала записи аудио:

Настройка MediaRecorder

Далее нам нужно указать путь для сохранения аудио и настроить MediaRecorder.

Мы берём путь к корню нашего внешнего хранилища и добавляем в него имя нашей записи и тип файла. После этого мы создаём объект MediaRecorder и определяем источник звука, аудиокодер, формат и файл для записи.

Запись и сохранение аудио

Код, используемый для запуска MediaRecorder , определяется в OnClickListener кнопки начала записи аудио:

Как видите, нужно вызвать функцию prepare , прежде чем мы сможем начать запись. Мы также встраиваем вызов в блок try-catch, чтобы приложение не сломалось при сбое функции prepare .

OnClickListeners кнопки остановки записи очень похож на код выше.

Здесь мы проверяем, работает ли в данный момент MediaRecorder , прежде чем мы остановим запись, потому что наше приложение сломается, если метод stop будет вызван, в то время как MediaRecorder не будет запущен. После этого мы меняем переменную состояния на false , чтобы пользователь не мог снова нажать кнопку остановки.

Нам осталось определить OnClickListener для кнопки приостановки/возобновления.

В этих двух методах мы проверяем, работает ли MediaRecorder . Если работает, мы приостановим запись и изменим текст кнопки для возобновления. При повторном нажатии запись возобновится.

Наконец, мы можем записать аудио и прослушать его, открыв файл recording.mp3 , который будет сохранён в нашем локальном хранилище. Просто откройте проводник файлов и сделайте поиск по имени файла recording.mp3 .

Исходный код

Вот полный исходный код нашего приложения:

Заключение

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

Более сложная версия этого приложения, в которой есть некоторые дополнительные функции, такие как воспроизведение ваших записей с помощью MediaPlayer , доступна на Github.

Источник

Полный список

— пишем звук с помощью AudioRecorder

Рассмотренный на прошлом уроке MediaRecorder записывал звук сразу в файл. AudioRecorder не пишет данные, а позволяет нам их получать в приложении. т.е. является посредником между приложением и микрофоном. Когда мы стартуем запись, AudioRecorder начинает получать данные с микрофона и хранит их у себя во внутреннем буфере. Мы можем при создании AudioRecorder указать желаемый размер этого буфера и далее запрашивать из него данные методом read.

Т.е. AudioRecorder будет полезен, если вы хотите как-то обработать данные перед записью в файл или, если вы хотите отправлять данные не в файл, а куда-либо еще.

Напишем приложение, в котором рассмотрим основные методы работы с AudioRecorder.

Project name: P1301_AudioRecorder
Build Target: Android 2.3.3
Application name: AudioRecorder
Package name: ru.startandroid.develop.p1301audiorecorder
Create Activity: MainActivity

Добавим строки в strings.xml:

Нарисуем экран main.xml:

Верхние кнопки стартуют/останавливают запись звука с микрофона в AudioRecorder, нижние – чтение данных из AudioRecorder в наш буфер.

В манифесте добавьте разрешение на запись звука: android.permission.RECORD_AUDIO.

В onCreate мы вызываем свой метод создания AudioRecorder и выводим в лог состояние созданного объекта. Состояние можно получить методом getState. Может быть всего два состояния: STATE_INITIALIZED и STATE_UNINITIALIZED. Означают они соответственно то, что AudioRecorder к работе готов или не готов.

В createAudioRecorder создаем AudioRecorder. Для этого нам понадобится несколько входных параметров:
— источник звука
— сэмплрейт
— режим каналов моно/стерео
— формат аудио
— размер буфера

Сэмплрейт мы ставим 8000. Режим — моно. Формат аудио – 16 бит. Чтобы узнать размер буфера, есть метод getMinBufferSize. Он, исходя из переданных ему на вход данных о формате аудио, возвращает минимально-возможный размер буфера, с которым сможет работать AudioRecorder. Мы получаем минимальный размер и в переменную internalBufferSize помещаем этот размер, умноженный на 4. Такой размер буфера будет в созданном AudioRecord.

Далее создаем AudioRecord. В качестве источника звука указываем микрофон. Также указываем сэмплрейт, режим каналов, формат и размер буфера.

В методе recordStart стартуем запись методом startRecording. С помощью метода getRecordingState получаем статус — идет запись или нет. Вариантов тут два: RECORDSTATE_RECORDING (запись идет) и RECORDSTATE_STOPPED (запись остановлена).

В recordStop останавливаем запись методом stop.

В readStart ставим метку isReading в true. Она будет означать, что мы сейчас находимся в режиме чтения данных из AudioRecorder. Далее создаем новый поток и чтение выполняем в нем, чтобы не занимать основной поток. Мы создаем свой буфер размером myBufferSize и читаем в него данные методом read. Это происходит в цикле, который проверяет, что мы в режиме чтения. Метод read на вход принимает массив (в который будут помещены данные), отступ (если вам надо прочесть данные не сначала, а с какой-то позиции), и размер порции получаемых данных. В readCount метод read возвращает число байт, которые он нам отдал. В totalCount мы суммируем общее количество полученных байтов.

У метода read кстати есть еще несколько реализаций, если эта вам не подходит. Подробнее смотрите в хелпе.

В методе readStop мы выключаем режим чтения, присваивая переменной isReading значение false. Поток из readStart прочтет это значение, выйдет из цикла и завершит свою работу.

В onDestroy выключаем режим чтения и методом release освобождаем ресурсы, занятые AudioRecord.

Все сохраняем, запускаем приложение.

В логах мы видим

minInternalBufferSize = 1024, internalBufferSize = 4096, myBufferSize = 8192
init state = 1

Мы видим, что минимальный размер буфера AudioRecorder насчитал равным 1024. Внутренний буфер будет 4096. Размер нашего буфера будет 8192. Состояние рекордера = 1, это значение переменной STATE_INITIALIZED, значит все ок, к работе готов.

Жмем Start record.

record start
recordingState = 3

Состояние записи рекордера = 3, это значение переменной RECORDSTATE_RECORDING, значит все ок, запись идет.

AudioRecorder сейчас получает данные с микрофона и держит их в своем буфере. Попробуем прочесть эти данные. Жмем Start read, и через несколько секунд Stop read

14:03:48.519: D/myLogs(14361): read start
14:03:48.519: D/myLogs(14361): readCount = 4096, totalCount = 4096
14:03:48.779: D/myLogs(14361): readCount = 4096, totalCount = 8192
14:03:49.039: D/myLogs(14361): readCount = 4096, totalCount = 12288
14:03:49.289: D/myLogs(14361): readCount = 4096, totalCount = 16384
14:03:49.549: D/myLogs(14361): readCount = 4096, totalCount = 20480
14:03:49.809: D/myLogs(14361): readCount = 4096, totalCount = 24576
14:03:50.069: D/myLogs(14361): readCount = 4096, totalCount = 28672
14:03:50.319: D/myLogs(14361): readCount = 4096, totalCount = 32768
14:03:50.569: D/myLogs(14361): readCount = 4096, totalCount = 36864
14:03:50.829: D/myLogs(14361): readCount = 4096, totalCount = 40960
14:03:51.079: D/myLogs(14361): readCount = 4096, totalCount = 45056
14:03:51.179: D/myLogs(14361): read stop
14:03:51.339: D/myLogs(14361): readCount = 4096, totalCount = 49152

Видим, что прошло несколько чтений буфера. Таким образом, мы получали данные с микрофона с помощью AudioRecorder.

Сколько байт занимает секунда звука

Разберемся, как подсчитать сколько места занимает звук, когда его пишешь. Вспоминаем наши параметры, которые мы задавали при создании AudioRecorder: сэмплрейт (как часто считывается звук) = 8000 Hz, формат (сколько места занимает одна запись) = 16 бит, канал = моно. Эти параметры означают, что одна секунда звука будет занимать 8000 * 16 * 1 = 128 000 бит = 16 000 байт.

Теперь посмотрим на логи. Первое чтение звука было в 14:03:48.519, а последнее в 14:03:51.339. Т.е. мы писали звук примерно три секунды. Три секунды – это 16 000 * 3 = 48 000 байт.

Снова смотрим логи, последнее значение totalCount = 49 152. Как видите, расчетная и реальная цифры похожи.

Давайте поменяем параметры в методе createAudioRecorder:

и снова посчитаем:

22 050 (сэмплрейт) * 16 (формат) * 2 (стерео) = 705 600 бит = 88 200 байт занимает теперь секунда звука. Как видите, при улучшении качества увеличились размеры.

Все сохраним, запускаем приложение. Жмем Start record, затем Start read и секунды через три Stop read.

14:15:29.959: D/myLogs(14567): read start
14:15:29.969: D/myLogs(14567): readCount = 8192, totalCount = 8192
14:15:29.969: D/myLogs(14567): readCount = 8192, totalCount = 16384
14:15:30.069: D/myLogs(14567): readCount = 8192, totalCount = 24576

14:15:32.479: D/myLogs(14567): readCount = 8192, totalCount = 237568
14:15:32.579: D/myLogs(14567): readCount = 8192, totalCount = 245760
14:15:32.659: D/myLogs(14567): read stop
14:15:32.669: D/myLogs(14567): readCount = 8192, totalCount = 253952

Видим, что примерно за 3 секунды мы считали 253 952 байта. Смотрим наши подсчеты 88 200 * 3 = 264 600. Цифры снова близки.

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

Слушатель отданных фреймов

Когда AudioRecorder отдает нам данные, он ведет подсчет отданных фреймов. Фрейм – это единица записи звука. Сколько байт занимает один фрейм? Это мы уже считали чуть раньше – формат * режим каналов. Т.е. если мы пишем звук в формате 16 бит/моно, то фрейм займет 16 * 1 = 16 бит = 2 байта.

Мы можем на AudioRecorder повесить слушателя с парой методов. Один метод будет срабатывать периодически через каждое указанное кол-во отданных фреймов, а другой разово по достижению определенного кол-ва отданных фреймов.

Метод setRecordPositionUpdateListener устанавливает слушателя с методами onPeriodicNotification и onMarkerReached, которые я описал выше. Метод setPositionNotificationPeriod устанавливает кол-во фреймов для срабатывания onPeriodicNotification, а setNotificationMarkerPosition – для срабатывания onMarkerReached.

Т.е. метод onPeriodicNotification будет срабатывать каждые 1000 отданных фреймов. А onMarkerReached – по достижению 10000 отданных фреймов. В onMarkerReached мы остановим чтение.

Все сохраним, запустим приложение и жмем Start record, затем Start read. В этот раз мы можем вручную не останавливать процесс чтения, т.к. он остановится сам по достижении 10000 фреймов.

read start
readCount = 4096, totalCount = 4096
onPeriodicNotification
onPeriodicNotification
onPeriodicNotification
readCount = 4096, totalCount = 8192
onPeriodicNotification
onPeriodicNotification
onPeriodicNotification
readCount = 4096, totalCount = 12288
onPeriodicNotification
onPeriodicNotification
readCount = 4096, totalCount = 16384
onPeriodicNotification
onMarkerReached
onPeriodicNotification
readCount = 4096, totalCount = 20480

Видим, что onPeriodicNotification срабатывал примерно каждые 2000 байт, а onMarkerReached сработал около получения 20000 байт.

Вспоминаем, что один фрейм при текущих настройках занимает два байта. А значит, как мы и указали, метод onPeriodicNotification срабатывал каждые 1000 фреймов, а onMarkerReached – как только было получено 10000 фреймов

На следующем уроке:

— получаем фото и видео, используя системное приложение

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

Читайте также:  Карты для андроид аэропорт
Оцените статью