• 0
Pax Beach

Запись и сохранение голоса в течение 10 секунд после запуска Android сервиса (Пример)

Вопросы

Я реализовал пример работы с микрофоном и таймером в Android сервисе, как указано в теме вопроса.

Каждый раз, когда вы отправляете в сервис StartCommand или сервис перезапускается, включается запись с микрофона, и сохраняется в каталог с музыков в файл "myrecord.3gp".

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

Для отладки своих программ на Android используйте запись в LOGI и чтение при помощи monitor.bat (PlatformSDKs\android-sdk-windows\tools).

 

uses
  ...
  AndroidApi.JNI.Media, // JMediaRecorder
  AndroidApi.Timer, // Timer
  ...;

Const
  TimerInterval = 1000;
  TimerCounterSecLimit = 10;

type
  TDM = class(TAndroidService)
    ...
  private
    FTimerHandle: Integer;
    FRecording: Boolean;

    procedure StartRecord;
    procedure StopRecord;

    procedure StartTimer;
    procedure StopTimer;
  public
    FAudioRec: JMediaRecorder;
  end;

procedure Log(const Fmt: string; const Params: array of const); overload;
var
  Msg: string;
  M: TMarshaller;
begin
  Msg := Format(Fmt, Params);
  LOGI(M.AsUtf8(Msg).ToPointer);
end;

procedure Log(const Source: string); overload;
var
  M: TMarshaller;
begin
  LOGI(M.AsUtf8(Source).ToPointer);
end;

procedure TDM.AndroidServiceCreate(Sender: TObject);
begin
  FTimerHandle := 0;
  FTimerCounter := 0;
  FRecording := false;
end;

procedure TDM.AndroidServiceDestroy(Sender: TObject);
begin
  StopTimer;
  StopRecord;
end;

function TDM.AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer;
begin
  if Intent.getAction.equalsIgnoreCase(StringToJString('StopIntent')) then
  begin
    StopTimer;
    StopRecord;
    Result := TJService.JavaClass.START_NOT_STICKY; // don't reload service
    Log('- service stoped', []);
  end
  else begin
    if not FRecording then
    begin
        Log('... sound record to be started', []);
        StartRecord;
        StartTimer;
    end;

    Result := TJService.JavaClass.START_STICKY; // rerun service if it stops
    Log('+ Service started', []);
  end;
end;

procedure TDM.StartRecord;
begin
  StopRecord;

  FAudioRec := TJMediaRecorder.Create;
  FAudioRec.setAudioSource(TJMediaRecorder_AudioSource.JavaClass.MIC);
  FAudioRec.setOutputFormat(TJMediaRecorder_OutputFormat.JavaClass.THREE_GPP);
  FAudioRec.setAudioEncoder(TJMediaRecorder_AudioEncoder.JavaClass.AMR_NB);
  FAudioRec.setOutputFile(StringToJString(TPath.Combine(TPath.GetSharedMusicPath, 'myrecord.3gp')));
  try
    FAudioRec.Prepare();
    FAudioRec.start;
    FRecording := True;
    Log('+ Start record to %s', [TPath.Combine(TPath.GetSharedMusicPath, 'myrecord.3gp')]);
  except
    on E: Exception do
      Log('- Error in mic recording: %s', [E.Message]);
  end;
end;

procedure TDM.StopRecord;
begin
  if Assigned(FAudioRec) then
  begin
    if FRecording then
    begin
      FRecording := false;
      try
        FAudioRec.stop();
        FAudioRec.release();
        Log('- Mic recording is stoped');
      except
        on E: Exception do
          Log('- Error in mic stop recording: %s', [E.Message]);
      end;
    end;
  end
  else
  begin
    FRecording := false;
  end;
end;

procedure TDM.WaitComplete(TimerId: Integer);
begin
  if FTimerCounter < TimerCounterSecLimit then
  begin
    Log('+++ Timer is triggered %d time.', [FTimerCounter]);
    inc(FTimerCounter);
  end
  else
    StopTimer;
end;

procedure TDM.StartTimer;
begin
  FTimerCounter := 0;
  if FTimerHandle = 0 then
  begin
    FTimerHandle := AndroidTimerCreate;
    AndroidTimerSetInterval(FTimerHandle, TimerInterval);
  end;
  AndroidTimerSetHandler(WaitComplete);

  Log('+ Timer started', []);
end;

procedure TDM.StopTimer;
begin
  if FTimerHandle > 0 then
  begin
    Log('... MIC recording to be stopped');
    StopRecord;

    AndroidTimerSetHandler(nil);
    Log('- Timer stoped', []);
  end;
end;

end.

 

Изменено пользователем Pax Beach
расширил ответ

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 ответа на этот вопрос

  • 0

Большое спасибо за такой пример!

Попробовал реализовать у себя - не создается файл. Все действия орабатываются, лог пишется (дополнял логированием после каждого шага - все в порядке). Сервис тоже "не падает" - работает. Но файла на выходе нет :( Ни по имени файла (с полным путем), ни по поиску файла на устройстве :(

Разрешения присутствуют (и на запись/чтение файлового хранилища и на запись аудио).

В чем может быть проблема?

RAD Studio 10.1 upd.2 Win8.1

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Сто пятьсот причин. Я бы плясал от версии андроид, наличие прав на сохранение файлов у приложения, куда приложение установлено, куда пишется файл.

Примерно в такой последовательности.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти

  • Похожий контент

    • От gutalin79
      Почему при добавлении MapView в пример Android Service, приложение перестает работать?
      AndroidSimpleService.zip
       
       
       
      Пример делал по этому видео: 
       
    • От AliZairov
      Здравствуйте. Кто-нибудь использует схему instagram api? Dev instagram Хотя я и сделал эксперименты, результат, к сожалению, не очень хороший.
      function getCode: string; begin FRequest.Method := TRESTRequestMethod.rmGET; FRequest.Resource := '/oauth/authorize/'; FRequest.AddParameter('client_id', client_id, TRESTRequestParameterKind.pkGETorPOST); FRequest.AddParameter('redirect_uri', URIEncode(redirect_uri), TRESTRequestParameterKind.pkGETorPOST); FRequest.AddParameter('response_type', 'code', TRESTRequestParameterKind.pkGETorPOST); FRequest.Execute; FRequest.Response.GetSimpleValue('code', Result); end; Проблема заключается в перенаправленном URL-адресе.
      http://your-redirect-uri?code=CODE
    • От AliZairov
      Привет. Я сделал новую версию YouTubeApi компоненты. Поддерживаемые VCL & FMX + Mobile.
      Никаких официальных YouTube Api компоненты. Только данные с парсинг. Пример доступен в архиве.
       
      Новый
      ===
      v1.4 - 25/02/2018
        + Устранение проблем
      YouTubeApiDelphi
    • От x11
      На основе нескольких тем форума сделал службу перехвата звонков и номера телефона.
      http://fire-monkey.ru/topic/3878-статьякак-создать-простой-android-broadcast-receiver-how-to-implement-simplest-android-broadcast-receiver-in-delphi/
      http://fire-monkey.ru/topic/2386-перехват-события-звонка/
      http://fire-monkey.ru/topic/2972-использование-нативных-localbroadcastmanager-и-broadcastreceiver/
      Покритикуйте пожалуйста. Уверен, что не всё правильно.
      Но что до ума не удалось довести, так это то, как правильно останавливать службу и закрывать приложение.
      Проект (для Токио) приложил теме.
      Заранее благодарен.
       
      BR2.rar
    • От M1shQa
      Подкинуло начальство  задачку, да не простую. Приложение должно в фоне мониторить gps, собирать данные о батарее, логировать и потом сливать на сервер. Все это будет нащадно жрать батарею, понимаю, но начальству нужен именно этот вариант. Перейду к вопросам:
      1.Нужно пилить сервис, но исходя из прошлых тем - таймер не работает, слипы не вариант, как сделать так, чтоб сервис просыпался каждые 5 минут/15 минут/ 1 час ?
      2.Мониторинг GPS, тут решение вроде нашел в этой ветке для seattle, как лучше всего собирать и хранить данные для отправки? (до этого все сохранял в простых .txt)
      3.Работают ли компоненты indy корректно в сервисе? срабатывают ли except'ы и как правильно мониторить подключение к сети? 
      Проблема еще и в том, что программа должна работать более менее стабильно и если опыт написания простых приложений у меня есть, то с сервисами уже пару месяцев не могу подружиться..
      Всем бобра!
    • От Равиль Зарипов (ZuBy)
      Компонент TMapsEngine
       
      Возможности
      Нативный LocationSensor [ANDROID] Геокодинг/Реверс геокодинг через Google API, Yandex API, Here Maps API Построение маршрута через Google Distance API Расчет дистанции по прямой Совместим со стандартным компонентом TLocationSensor  
      Видео демонстрация
       
      На видео видно как TMapView косячик с миллионом точек, приложение зависло при отрисовке, но с этим ничего не поделать.
      Без синхронизации не возможно отрисовать все точки
       
      Исходник
      TMapsEngine_source.zip
      TMapsEngine_Seattle_10.zip
       
      Скачать APK  (долго он там не пролежит, а форум не позволяет загрузить zip размером 7,4 мб )
       
    • От Равиль Зарипов (ZuBy)
      Доброго времени суток!
      Навеяно этой темой.
      Посмотрел реализацию от @Pax Beach (за что ему спасибо большое) и столкнулся с тем что TRESTClient со всеми потрахами это просто дикий ужас.
      Решил собрать аналогичный пример, только не использовать TREST***
      немного опишу, свою реализацию
      uAppSettings.pas - хранит описание вашего приложения созданного в ВК  FMX.RESTLight.pas - модуль "общения" клиента с сервером FMX.RESTLight.Types.pas - тут хранятся типы для работы TRESTLight Приведу код, который постит Вам на стену текст
      var aFields: TArray<TmyRestParam>; aJSON: string; begin SetLength(aFields, 5); aFields[0] := TmyRestParam.Create('access_token', FAuthToken.token, false); aFields[1] := TmyRestParam.Create('v', FVKApp.APIVersion, false); aFields[2] := TmyRestParam.Create('owner_id', FAuthToken.user_id, false); aFields[3] := TmyRestParam.Create('friends_only', '0', false); aFields[4] := TmyRestParam.Create('message', 'Тестовое сообщение <RESTLight>', false); TTask.Run( procedure begin aJSON := TRESTLight.Execute('wall.post', FVKApp, aFields); TThread.Synchronize(TThread.CurrentThread, procedure begin Memo1.Lines.Add('---- wall.post ----'); Memo1.Lines.Add(aJSON); end); end); а вот так можно заливать файлы
      var aFields: TArray<TmyRestParam>; aJSON: string; begin SetLength(aFields, 4); aFields[0] := TmyRestParam.Create('access_token', FAuthToken.token, false); aFields[1] := TmyRestParam.Create('v', FVKApp.APIVersion, false); aFields[2] := TmyRestParam.Create('owner_id', FAuthToken.user_id, false); aFields[3] := TmyRestParam.Create('photo', aUploadFile, true); // для файлов указывается !!!true!!! TTask.Run( procedure begin // для загрузки файлов использовать TRESTLight.Execute2, в нем можно указать произвольный URL aJSON := TRESTLight.Execute2(aUploadURL, FVKApp, aFields); TThread.Synchronize(TThread.CurrentThread, procedure begin Memo1.Lines.Add('---- upload file to server ----'); Memo1.Lines.Add(aJSON); end); end); P.S. На правильность и оригинальность не претендую, просто альтернативный вариант. Мне например не понравилась как устроен TRESTClient, много компонентов лишних. В боевых условиях наверное код еще будет модернизироваться, я рассмотрел только пару методов, но думаю общая концепция не сильно поменяется
      GitHub   TRESTLight.zip
    • От Rusland
      Проблема давняя, хочется наконец разобраться с ней.
      Вот создал тестовый пример, в котором приложение TestPro и локальный Android-сервис к нему TestService2.
      Запускаю программу, нажимаю кнопку Start Service, сервис стартует (увидеть можно в Настройки -> Приложения -> Работающие).
      Не закрывая программу, перехожу к другим программам и работаю в них. Лучше всего перейти в какой-нибудь браузер и полазить по вкладкам (это довольно ресурсоемкое приложение). 
      После нескольких минут через диспетчер задач я возвращаюсь в свое приложение и оно начинает загружаться с нуля. То есть система давно прибила программу и она начинает загрузку заново. Так вот программа долго висит на заставке, а потом выходит сообщение что приложение не отвечает! Нажимаем OK. После повторного перезапуска программы происходит тоже самое и лишь на третий раз программа запускается.
      Как решить эту проблему?
       
      Я пробовал убивать сервис
      var LIntent: JIntent; LIntent := TJIntent.Create; LIntent.setClassName(TAndroidHelper.Context.getPackageName(), TAndroidHelper.StringToJString('com.embarcadero.services.TestService2')); TAndroidHelper.Activity.stopService(LIntent); до строчки Application.Initialize - не помогает.
       
      TestService2.zip
    • От Pax Beach
      Создан проект в RAD Studio 10.1 Berlin (Delphi), в приложении прилинкован сервис. При компиляции проекта в AndroidManifest.xml появляется строчка:
      <service android:exported="true" android:name="com.embarcadero.services.MyService" /> А мне нужна строчка:
      <service android:exported="true" android:name="com.embarcadero.services.MyService" android:label="Этот замечательный сервис создан вручную"/> Ни где не нашел такое свойство. В Project->Options->Version info есть свойство Label только для самого Host приложения.
      Помогите пожалуйста советом.
    • От Rusland
      Пытаюсь из сервиса вывести лог, но в monitor ничего не приходит.
      Для примера вот тестовый проект
      Проверьте, как у вас?
      PS. Из приложения лог приходит нормально
  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу