반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

코로나 네트워킹 과 웹 서비스 1

2011. 10. 21. 23:27 | Posted by 솔웅


반응형
Networking and Web Services

코로나는 웹 서비스를 위해서 LuaSocket library 2.02버전을 사용합니다.
이 루아 소켓 라이브러리는 SMTP, HTTP, FTP같은 프로토콜을 지원합니다. 이 외에 인코딩 프로토콜인 MIME와 URL 사용 그리고 LTN12 (데이터 트랜스퍼와 필터링) 기능도 지원합니다.
이 Luasocket은 코로나 앱에 이미 인스톨 되 있어서 director.lua나 ui.lua 같이 따로 파일을 복사해 넣을 필요는 없습니다. 하지만 앱실행시의 퍼포먼스 문제로 자동으로 로드되지는 않습니다.
그러므로 이것을 사용하기 위해서는 우선 require 해야 합니다.
local socket=require("socket") 이나 local http = require("http") 같이 먼저 선언하고 난 다음에 사용할 수 있습니다.
LuaSocket 문서는 아래 링크에서 볼 수 있습니다.

http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/reference.html

아래 샘플 코드가 있습니다.
-- Load the relevant LuaSocket modules
local http = require("socket.http")
local ltn12 = require("ltn12")
 
-- Create local file for saving data
local path = system.pathForFile( "hello.png", system.DocumentsDirectory )
myFile = io.open( path, "w+b" )
 
-- Request remote file and save data to local file
http.request{
    url = "http://developer.anscamobile.com/demo/hello.png",
    sink = ltn12.sink.file(myFile),
}
 
-- Display local file
testImage = display.newImage("hello.png",system.DocumentsDirectory,60,50);

print("hello.png image is in " .. path)


일단 http 통신을 하기 위해서 socket.http를 require하고 파일을 transfer하기 위해서 ltn12를 require했습니다.
그 다음 http.request{} 부분을 보면 이미지가 있는 url을 지정하고 이 파일을 미리 지정해 두었던 myFile에 hello.png라는 이름으로 저장합니다.
io.open 으로 미리 쓰기 가능 상태로 두었습니다.
이 path는 device의 기본 폴더입니다. 캡쳐 화면의 터미널 부분을 보면 애플 컴퓨터의 디렉토리가 나오는데요. 아이폰으로 하면 아이폰 폴더 경로가 나올 겁니다.

여기 까지 하면 원격에 있는 이미지를 내 핸드폰으로 가져오는 것까지 성공 한 겁니다.
일단 가져왔으니까 그 다음엔 그냥 보여주면 되겠죠?
display.newImage() 를 통해서 이미지를 폰에 뿌려줍니다.

안드로이드에서는 인터넷을 하려며 퍼미션 (Permissions)를 주어야 합니다.
이 퍼미션은 build.settings 파일에서 설정해 줍니다.

-- build.settings
settings =
{
    androidPermissions =
    {
         "android.permission.INTERNET",
    },
}

광고 ( Ads)
광고는 이전에도 잠깐 다룬적이 있습니다.
현재 코로나는 inMobi 광고만 공식적으로 지원하고 있습니다.
그리고 광고는 시뮬레이터에서 지원을 안하기 때문에 빌드하고 디바이스에 인스톨 한 다음에 테스트 하셔야 됩니다.

아래 샘플 코드를 보시면요.

local ads = require "ads"
 
-- Example for inmobi:
ads.init( "inmobi", "12345abc6789defghijk123" )
 
-- iPhone, iPod touch, iPad
ads.show( "banner320x48", { x=0, y=100, interval=5, testMode=false } )
ads.show( "banner300x250", { x=0, y=100, interval=5, testMode=false } )
 
-- The following are iPad only
ads.show( "banner728x90", { x=0, y=100, interval=5, testMode=false } )
ads.show( "banner468x60", { x=0, y=100, interval=5, testMode=false } )
ads.show( "banner120x600", { x=0, y=100, interval=5, testMode=false } )

처음에 ads를 require합니다.
그리고 ads.init을 불러와서 광고서비스 이름과, App ID 를 세팅하고
ads.show로 적당한 위치에 제공되어지는 배너 크기를 정해 넣습니다.
광고를 보이지 않게 하기 위해선 ads.hide()를 사용합니다.

이 때 테스트를 하시려면요.
App ID 에 4028cb962895efc50128fc99d4b7025b 를 넣으시구요.
testMode=true 로 하세요.

그리고 직접 등록해서 사용하시려면 www.inmobi.com 으로 가셔서 등록하신 후 사용하시구요.

아래는 제 안드로이드 앱인 Spin the bottle 1 Lite 를 코로나로 iPhone 버전으로 만든겁니다.

이 화면은 ads.show() 를 이용해서 banner320x48 를 맨 위에 위치 시킨 겁니다.

이 화면은 ads.show()를 이용해서 banner320x48 를 맨 아래에 위치 시킨 겁니다.

이 화면은 ads.show()를 이용해서 banner300x250 를 적당한 위치에 놓은 겁니다.

지금 아직 앱스토어에서 Waiting for Review 상태라서 앱 URL을 inMobi 에 등록 시키지 못했습니다.
아마 앱이 앱스토어에 오픈 되고 이 URL이 확인 된 다음에 광고를 보내 주나 봅니다.

저희 회사에서 테스트할 수 있는 디바이스가 iPod Touch뿐이라서요. 다른건 테스트를 못 해봤습니다. (테스트 디바이스 지원이 너무 열악해요. 우리 회사... ;;)

iPod Touch 에서는 화면이 뜬 다음에 아직 광고가 나오지 않은 상태에서 다른 화면으로 넘어가면 에러가 나오면서 앱이 강제종료 되더라구요.

이에 iPod Touch에서만 나오는 에러인지 아니면 이런 에러를 방지하는 코딩이 따로 있어야 되는건지는 잘 모르겠어요.

다른 디바이스에서도 테스트를 해 봐야 겠는데....

하옇든 아래 코로나에서 제공하는 Ads관련 샘플 코드를 분석 해 보겠습니다.
혹시 이에 대한 해결책이 있는지 한번 볼까요?

--==================================================================================================
--
-- Abstract: InMobi Ads Sample Project
--
-- This project demonstrates Corona Banner Ads support (from inmobi network).
--
-- IMPORTANT: You must get your own "app ID" from the advertising
-- agency you want to display banner ads for. Further, you must build for device
-- to properly test this sample, because "ads" library is not supported by the
-- Corona Simulator.
--
--   1. Get your app ID (example, from inmobi)
--   2. Modify the code below to use your own app ID
--   3. Build and deploy on device
--
-- The code below demonstrates the different banner ads you can use
-- with the InMobi ad network.
-- Version: 1.0 (July 7, 2011)
-- Version: 1.1 (July 22, 2011) - Added Hide button and changed Next button behavior.
--
-- Sample code is MIT licensed, see http://developer.anscamobile.com/code/license
-- Copyright (C) 2011 ANSCA Inc. All Rights Reserved.
--
--==================================================================================================


--==================================================================================================
-- INITIAL SETTINGS
----------------------------------------------------------------------------------------------------

-- hide the status bar:
display.setStatusBar( display.HiddenStatusBar )

-- Below is the ad network that will be used:

local adNetwork = "inmobi"

-- Replace nil below with your app ID:
-- String; e.g. surrounded by quotes (ex. "abcdefghijklmnop")

local appID = "4028cb962895efc50128fc99d4b7025b"

--==================================================================================================

-- Make Banner Ads features available under "ads" namespace
local ads = require "ads"

-- initialize ad network:
if appID then
    ads.init( adNetwork, appID )
end

-- initial variables
local sysModel = system.getInfo("model")
local sysEnv = system.getInfo("environment")

local bgW, bgH = 320, 480
local currentAdIndex = 1

local adsTable = {
    "banner320x48",
    "banner300x250",
}
if sysModel == "iPad" then
    -- change settings if on iPad. It has 3 additional adUnitTypes it can show.
    bgW, bgH = 768, 1024
    adsTable = {
        "banner320x48",
        "banner300x250",
        "banner728x90",
        "banner468x60",
        "banner120x600"
    }
end

-- localize a widget function
local newButton = require( "widget" ).newButton

-- change settings if on iPad
if sysModel == "iPad" then
    bgW, bgH = 768, 1024
end

-- create a background for the app
local backgroundImg = display.newImageRect( "space.png", bgW, bgH )
backgroundImg:setReferencePoint( display.TopLeftReferencePoint )
backgroundImg.x, backgroundImg.y = 0, 0


if appID then
    -- Shows the banner indexed by variable "currentAdIndex"
    local showIndexedBanner = function()
        print("Showing Banner: " .. adsTable[currentAdIndex])
        local adX, adY = 0, 0
        ads.show( adsTable[currentAdIndex], { x=adX, y=adY, interval=5, testMode=true } )
    end

    -- onRelease event listener for 'nextButton'
    local onNextButtonReleased = function( event )
        currentAdIndex = currentAdIndex + 1
        if (currentAdIndex > #adsTable) then
            currentAdIndex = 1
        end
        showIndexedBanner()
    end

    -- onRelease event listener for 'hideButton'
    local onHideButtonReleased = function( event )
        ads.hide()
    end

    -- if on simulator, make sure onRelease event for buttons are set to nil
    if sysEnv == "simulator" then
        onNextButtonReleased = nil
        onHideButtonReleased = nil
    end

    -- create a next button (to show a different ad unit)
    local nextButton = newButton{
        default="button.png",
        over="button_over.png",
        label = "Show Next Banner",
        onRelease= onNextButtonReleased
    }
    nextButton:setReferencePoint( display.CenterReferencePoint )
    nextButton.x = display.contentWidth * 0.5
    nextButton.y = display.contentHeight - 120

    -- create a hide button
    local hideButton = newButton{
        default="button.png",
        over="button_over.png",
        label = "Hide Banner",
        onRelease= onHideButtonReleased
    }
    hideButton:setReferencePoint( display.CenterReferencePoint )
    hideButton.x = display.contentWidth * 0.5
    hideButton.y = display.contentHeight - 60

    -- if on simulator, let user know they must build for device
    if sysEnv == "simulator" then
       
        local font, size = "Helvetica-Bold", 22
       
        local warningText1 = display.newText( "Please build for device ", 0, 135, font, size )
        local warningText2 = display.newText( "to test this sample code.", 0, 165, font, size )
       
        warningText1:setTextColor( 255, 255, 255, 255 )
        warningText2:setTextColor( 255, 255, 255, 255 )
       
        warningText1:setReferencePoint( display.CenterReferencePoint )
        warningText2:setReferencePoint( display.CenterReferencePoint )
       
        local halfW = display.contentWidth * 0.5
        warningText1.x, warningText2.x = halfW, halfW
       
        -- make buttons appear disabled
        nextButton.view.alpha = 0
        hideButton.view.alpha = 0
    else
        -- display initial banner ad
        showIndexedBanner()
    end
else
    -- If no appId is set, show a message on the screen
    local font, size = "Helvetica-Bold", 22

    local warningText1 = display.newText( "No appID has been set.", 0, 105, font, size )
    warningText1:setTextColor( 255, 255, 255, 255 )
    warningText1:setReferencePoint( display.CenterReferencePoint )

    local halfW = display.contentWidth * 0.5
    warningText1.x = halfW

    if sysEnv == "simulator" then
        local warningText2 = display.newText( "Please build for device ", 0, 185, font, size )
        local warningText3 = display.newText( "to test this sample code.", 0, 215, font, size )
        warningText2:setTextColor( 255, 255, 255, 255 )
        warningText3:setTextColor( 255, 255, 255, 255 )

        warningText2:setReferencePoint( display.CenterReferencePoint )
        warningText3:setReferencePoint( display.CenterReferencePoint )
        warningText2.x, warningText3.x = halfW, halfW
    end
end

이 샘플 코드는 Show Next Banner를 누르면 해당 디바이스에서 보일 수 있는 배너 크기별 광고를 차례대로 보여줍니다.
그리고 Hide Banner를 누르면 이 배너를 감춥니다.

코드를 라인별로 분석해 볼까요?
(아래 압축파일에 원본소스와 이미지 등이 있습니다.)



우선 주석은 다 지나가고 34번째 줄부터 시작합니다. 이 줄은 아이폰의 statusBar를 없애는 부분입니다.
39번째 줄에 adNetwork라는 변수에 inmobi라는 값을 담았습니다.
그리고 appID에 inmobi에서 받은 값을 넣어야 하는데요. 위 소스는 nil로 돼 있어서 저는 테스트용 id를 넣었습니다. "4028cb962895efc50128fc99d4b7025b"

그 다음 49번째 줄에서 ads를 require 했고 다음 줄(52)에서 appID가 nil이 아니면 ads.init을 해 줍니다.

그리고 sysModel에 디바이스의 모델을 그리고 sysEnv에 디바이스의 environment를 담습니다.

61번째 줄을 보면 bgW,bgH를 아이폰 해상도에 맞게 320,480 으로 정합니다.
다음줄에 currentAdIndex 에 1을 대입합니다.

다음 adsTable이라는 테이블에 아이폰에서 가능한 배너크기 두가지를 넣습니다.
그리고 sysModel이 iPad 이면 bgW,bgH에 768,1024 를 대입하고 adsTable에는 5종류의 배너크기 모두를 대입합니다.

82번째 줄은 newButton을 wedget의 버튼으로 선언합니다.

85~88번째 줄은 iPad일 경우 bgW,bgH를 바꿔 주는 부분인데요. 70번째 줄에서 이미 한 작업인데 여기서 또 하네요. 이건 없애도 되겠습니다.

다음 92~94째줄은 backgroundImg를 세팅해 주는 부분입니다.

이제 다음부터 어떤 로직이 나오나 봅니다. (저도 지금 이거 처음 보면서 작성하는 거라서 뭐가 나올지 모릅니다.)

appID가 있으면  local showIndexedBanner 함수를 선언하는데요. adX,adY를 0,0으로 하고 ads.show()를 통해서 광고를 보여줍니다.

그 다음 onNextButtonReleased 함수가 있는데 파라미터로 event를 받는 거로 봐서 어떤 리스너에서 호출할 건가 봅니다.
여기서는 currentAdIndex에 1을 더해주고 adsTable의 요소보다 커지게 되면 다시 currentAdIndex를 1로 해주는 로직이 있구요.
이 작업이 끝나면 showIndexedBanner()를 호출합니다.

그러니까 어떤 버튼이 클릭되면 currentAdIndex를 바꿔서 showIndexedBanner()함수를 호출하니까 adsTable에 있는 배너 종류가 차례대로 보이겠네요.

그 다음 117번째 줄은 onHideButtonReleased 함수가 있는데 이것도 event 파라미터가 있는 걸로 봐서 리스너에서 호출 할 겁니다.
당연히 hide ads버튼에서 호출하겠죠?
이 함수가 호출되면 ads.hide()를 실행 시켜서 광고를 없앱니다.

그 다음 123번째 줄은 sysEnv가 시뮬레이터면 onNextButtonReleased와 onHideButtonReleased 함수를 모두 없앱니다.

130번째 줄 nextButton 에는 default button image와 over시 버튼 이미지를 정해주고 글자를 Show Next Banner라고 정해 줍니다. onRelease 에는 onNextButtonReleased를 대입해 줍니다.
그리고 그 위치를 정해 주고요.
142번째 줄에서는 hideButton을 선언해주고 위치를 정해 줍니다.

154번째 줄에서는 sysEnv가 시뮬레이터 이면 warningText1,warningText2를 뿌려줍니다.
그리고 두개의 버튼에 투명도를 주어서 안 보이도록 합니다.
시뮬레이터가 아니면 showIndexedBanner()를 실행 시켜서 광고를 보여줍니다.
버튼에 투명도 처리하는 부분도 적용 안 되니까 이 경우엔 버튼도 보이겠죠?

177번째 줄은 저 위에 있는 if문하고 연결 됩니다 뭐냐하면 97번째 줄의 if appID then 요.

만약 appID가 없다면 97번째 이후에 있던 코드는 다 실행하지 않고 181번째 줄에 있는 warningText1을 화면에 표시합니다.
그리고 시뮬레이터 이면 warningText2,warningText3를 출력합니다.

여기까지가 이 소스코드를 모두 분석한 내용입니다.

제가 iPod Touch에서 겪었던 에러 (화면 뜨고 광고가 뜨기 전에 화면이동-director.lua- 할 경우 생기는) 를 해결 할 수 있는 부분은 없네요.

아직 코로나에서 미처 대응하지 못했던 에러가 아닌가 합니다.
iPhone이나 그 이후 버전의 phone이나 iPad에서는 이러한 현상이 일어나는지 어떤지 잘 모르겠습니다.

이거 회사 테스트 디바이스 지원이 너무 열악해요...

하여간 오늘은 간단히 소켓통신에 대해 알아봤구요.
그리고 코로나에서의 광고 띄우기 (inMobi) 에 대해 자세히 알아봤습니다.

다음엔 네트워킹과 web service에 대해 마저 알아볼 건데요.
요즘 제가 관심 가지고 있는 In-App Purchases 에 대해서도 있네요.

시간이 허락되면 여기까지 다 공부 하겠습니다.

반응형

오디오, 비디오, 사진 컨트롤 2

2011. 10. 20. 22:51 | Posted by 솔웅


반응형
비디오 라이브러리

비디오 파일 플레이는 media.playVideo 함수로 구현 되고 이것은 비동기적입니다.
비디오가 끝나거나 유저가 끝낼을 때 어떤 핸들링을 하려면 onComplete를 사용합니다.
local onComplete = function(event)
   print( "video session ended" )
end
media.playVideo( "Movie.m4v", true, onComplete )
비디오 파일 플레이는 remote URL을 이용해 상영할 수도 있습니다.
신택스는 아래와 같습니다.
media.playVideo( path [, baseSource ], showControls, listener )

baseSource 부분은 옵션으로 디폴트는 system.ResourceDirectory입니다. URL을 넣을 수도 있습니다.

코로나에서 샘플로 제공하는 샘플 코드인 StreamingVideo 소스 코드를 보면 아래와 같습니다.

display.setStatusBar( display.HiddenStatusBar )   -- 아이폰의 StatusBar를 없앤다.
local posterFrame = display.newImage( "Default.png" )
function posterFrame:tap( event ) -- posterFrame이미지를 tap하면 실행되는 함수
    msg.text = "Video Done"        -- message will appear after the video finishes
    media.playVideo( "http://www.anscamobile.com/video/Corona-iPhone.m4v", media.RemoteSource, true )
end
-- Determine if running on Corona Simulator
-- 현재 device가 simulator인지 체크 함
local isSimulator = "simulator" == system.getInfo("environment")
-- Video is not supported on Simulator
if isSimulator then -- 시뮬레이터라면 이 메세지를 화면에 뿌림
    msg = display.newText( "No Video on Simulator!", 0, 60, "Verdana-Bold", 22 )
else -- 시뮬레이터가 아니면 아래 메세지를 뿌림
    msg = display.newText( "Tap to start video", 0, 60, "Verdana-Bold", 22 )
    posterFrame:addEventListener( "tap", posterFrame )        -- add Tap listener
end
msg.x = display.contentWidth/2        -- center title
msg:setTextColor( 0,0,255 )



비디오 파일 플레이하는것도 보시다시피 아주 간단합니다.

코로나에서 지원하는 비디오파일 포맷은 아래와 같습니다.
.mov, .mp4, .m4v, .3gp

카메라와 사진 라이브러리

media.show(imageSource,listener)
imageSource 부분엔 아래 세가지 중 하나가 들어갑니다.
media.PhotoLibrary
media.Camera
media.SavedPhotosAlbum

이 함수는 비동기적입니다. 그 의미는 이 함수 다음에 어떤 메소드(함수)가 있다면 이 함수가 끝난 이후에 실행 될거라는 겁니다.
아래 소스를 참고하세요.
local onComplete = function(event)
   local photo = event.target
   print( "photo w,h = " .. photo.width .. "," .. photo.height )
end
media.show( media.Camera, onComplete )

Event Sounds

media 라이브러리는 재생을 위해  아래기능을 지원합니다.
event sounds : 짧은 소리, 전체가 재생 됨
extended sounds : 긴 소리. 재생 기간 동안 정지 할 수도 있음. 한번에 하나만 open 할 수 있음

Event Sounds

1~3초 정도의 짧은 소리가 있다면 event sound API를 사용하는 것이 좋습니다.
local soundID = media.newEventSound( "beep.caf" )
 
local playBeep = function()
        media.playEventSound( soundID )
end
timer.performWithDelay( 1000, playBeep, 0 )

이 사운드 파일은 media.newEventSound를 통해 한번 로딩 된 후 여러번 반복해서 사용 될 수 있습니다.

media.newEventSound(soundFile) : soundFile로부터 sound를 로딩합니다. 그리고 sound id 이벤트를 리턴합니다. 이것은 media.playEventSound에 전달 될 인수입니다.
media.playEventSound(sound) : 사운드를 재생합니다.

안드로이드에서는 사운드를 로딩하고 준비하는데 약간의 딜레이가 있을 수 있습니다. 그러므로 playEventSound에 filename 파라미터를 사용하는 것은 추천하지 않습니다. newEventSound로 미리 로딩해서 사용하세요.

playEventSound 안에 아래와 같이 함수를 지정할 수 있습니다.
media.playEventSound("beef.caf",onComplete)
이렇게 되면 플레이가 끝난 이후 onComplete함수를 실행합니다.

Extended Sounds

좀 긴 사운드 (아이폰에서의 MP3 포맷이나 배경음악 재생같은)에 사용됩니다.
play,pause,stop같은 기능들이 있습니다.

media.playSound( "song.mp3" )
 
local stopAfter10Seconds = function()
        media.stopSound()
end
timer.performWithDelay( 10000, stopAfter10Seconds )

media.stopSound()와 media.playSound(soundFile) 은 위에 있구요.
이외에 media.pauseSound() 도 있습니다.

아주 큰 사운드 파일일 경우는 로딩하는데 시간이 오래 걸릴 수 있습니다. 이 경우 애니메이션이 실행 되고 있다면 잠깐 중단 될 수도 있습니다. 이를 방지 하기 위해서 애니메이션이 실행되기 이전에 로딩해 둘 수 있겠죠?

media.playSound( 'sound.mp3' )
media.stopSound()

이렇게 play시켰다가 곧바로 stop 시키면 미리 로딩을 시킬 수 있을 겁니다.
어쩐지 좀 꼼수 같죠? 코로나 Doc에서 정식으로 이 방법을 소개하고 있네요.

아래와 같이 사운드 종료시 다른 함수를 실행 시키도록 할 수 있습니다.
local onComplete
onComplete = function(event)
        print( "sound play ended" )
        media.playSound( "note2.mp3", onComplete )
end
 
media.playSound( "note2.mp3", onComplete )

이 코드는 note2.mp3가 끊이지 않고 계속 루핑되면서 재생되는 효과가 있겠네요.
루핑기능을 위한 꼼수로 보이네요.

Loop Parameter
media.playSound("note2.mp3", true)
이렇게 마지막에 true 파라미터를 주면 사운드를 계속 반복 재생시켜줍니다.
이 방법이 조 위에 있는 방법보다 부담을 덜 줄겁니다.

media.setSoundVolume(0.5)
print("volume = "  .. media.getSoundVolume())
위와 같이 볼륨 조절이 가능합니다.

event sound에는 기 볼륨조절 기능이 지원이 안 됩니다. 이건 iPhone API에서 제공하지 않는다고 하네요.

코로나 SDK에서 지원하는 Audio Format들은 아래와 같습니다.
event sound file
- 수초 이내의 짧은 시간이어야 한다.
- PCM,IMA4(IMA/ADPCM) 포맷
- .caf나 .aif 파일
media.playSound()를 이용하는 좀 더 긴 파일은 mp3파일을 지원 합니다.

녹음 기능
recording = media.newRecording([file])
recording:startRecording()
recording:stopRecording()

녹음에는 이런 기능들이 제공 됩니다.
이건 시뮬레이터나 iPod Touch 같이 하드웨어에 마이크(녹음) 기능이 없으면 테스트가 불가능하겠죠.
그러면 빌드를 해서 직접 폰에서 테스트를 해야 할 텐데 그러려면 코로나 유료버전을 사야 되구요.
테스트 하기엔 좀 제한이 있네요.

result = recording:isRecording()
현재 녹음 되고 있다면 true를 그렇지 않으면 false를 반환합니다.

recording:setSampleRate() ; rate = recording:getSampleRate()

디폴트는 44100dlqslek. 8000,11025,16000,22050,44100 Rate 등이 검증 된 수치들 입니다.
startTuner()를 하기 전에 반드시 이 setSampleRate()를 불러와야 합니다.
사운드 관련된 전문 앱을 만드려면 이런걸 신경 쓰셔야 겠지만 또 하드웨어 적으로 지원이 안되면 이것도 한계가 있을 겁니다.

Audio Tuner
recording:startTuner()
tuning을 할 수 있도록 합니다.

freq = recording:getTunerFrequency()
Hz 를 반환합니다. 이 기능은 tuner가 on 인 상태에서만 작동 됩니다.

recording:stopTuner() : tuner를 정지 합니다.

volume = recording:getTunerVolume()
최근에 확정된 볼륨 숫자를 반환합니다. 범위가 -1에서 1사이로 나온다고 하는데요.
이것을 알기 쉽게 보려면 10*math.log(volume)을 통해서 보는게 좋습니다.

자 그럼 Corona SDK 의 Media 기능에 대해 다 훑어 봤습니다.

이제 남은 주제들은 Networking and Web Service, System and OS, Interactivityand Detecting Events, Location and Maps, Native UI, Advanced Programming Techniques 등이 남았네요.

꽤 많이 한것 같은데 아직 다뤄야 할 이슈들이 많습니다.
부지런히 다루고 그 다음은 샘플 코드들을 분석해 볼까 합니다.
여러가지 TIP들도 많이 다뤄보구요.

다음 시간에 뵐께요.


 

반응형

오디오, 비디오, 사진 컨트롤 1

2011. 10. 19. 23:18 | Posted by 솔웅


반응형
이제 오디오, 비디오, 사진 컨트롤을 코로나에서는 어떻게 하는지 공부하겠습니다.
첫번째로 오디오에 대해서 알아보겠습니다.

안드로이드 앱을 만들어 보신 분들은 아시겠지만 안드로이드에서는 이미지나 오디오 파일, xml 파일을 인식할 때 확장자는 고려하지 않습니다.
그러니까 aaa.png나 aaa.gif 나 aaa.jpg 이렇게 확장자만 다르고 이름이 같은 파일들은 동일하게 인식을 합니다. (결국엔 에러를 일으키게 됩니다.)
오디오도 마찬가지 인데요. aac, aif, caf, mp3, ogg 등이 오디오에서 사용하는 확장자 인가봅니다. 이름을 정할 때 확장자를 제외한 파일 이름이 유니크 하도록 정해야 합니다.

코로나에서 오디오를 다룰 때 사용하는 채널의 맥시멈은 32 입니다.
그러니까 동시에 32 channel 까지 사용할 수 있습니다.
audio.totalChannels 를 통해서 채널 갯수를 알 수 있습니다.

사운드(sound) 를 load하는 방법은 두가지가 있습니다.
loadSound()와 loadStream() 입니다.
첫번째는 사운드 파일을 모두 메모리에 올려 놓는 것이고 두번째는 play 할 때 스트리밍 하면서 플레이 하는 것입니다.
첫번째는 시간상으로 절약이 되는 대신에 메모리를 많이 차지하겠고 두번째는 시간이 지연될 수는 있지만 메모리를 아낄 수 있겠죠?

위 파일을 받아보세요.
제가 스나이퍼의 총소리를 좋아하는데 그거 비슷한 소리 같아서 자주 이용해요.
그리고 main.lua에 아래 코드를 넣어보세요.
 explosionSound = audio.loadSound("explosion6.wav")
  audio.play(explosionSound)
  audio.play(explosionSound)
  audio.play(explosionSound)
  audio.play(explosionSound)
  audio.play(explosionSound)

audio.play를 한개만 했을 때와 위와 같이 여러개 했을 때와 비교해 보세요.
위와 같이 loadSound로 불러들이면 메모리에 사운드를 로딩해 놓고 있는 상황이기 때문에 동시에 사운드를 들려줍니다.
audio.loadSound 를 audio.loadStream으로 바꾸면 어떻게 될까요?
이 경우엔 audio.play 하면서 파일을 메모리로 일부분씩 불러들이고 (streaming 하고) 소리를 내기때문에 1개의 소리만 들립니다.

loadStream을 하면서 동시에 소리를 내고 싶으면 아래와 같이 해야 됩니다.
explosionSound1 = audio.loadStream("explosion6.wav")
explosionSound2 = audio.loadStream("explosion6.wav")
explosionSound3 = audio.loadStream("explosion6.wav")
explosionSound4 = audio.loadStream("explosion6.wav")
explosionSound5 = audio.loadStream("explosion6.wav")
  audio.play(explosionSound1)
  audio.play(explosionSound2)
  audio.play(explosionSound3)
  audio.play(explosionSound4)
  audio.play(explosionSound5)

이렇게 하니까 동시에 소리가 나네요.

loadSound로 불러들이면 미리 메모리에 올려놓은 상태이기 때문에 즉시 소리를 play할 수 있습니다. 반면에 loadStream을 사용하면 첫번째 chunk를 메모리로 불러들이는데 시간이 좀 걸릴 수 있습니다.

audio.play를 다 하고 메모리를 비우려면 audio.dispose() 함수를 사용해야 합니다.
물론 해당 오디오 파일이 프로그램 끝날 때 까지 계속 사용해야 되면 이 함수를 사용할 일은 없겠지만요. 당연히 프로그램이 종료 될 때 모든 메모리는 릴리즈 됩니다.

audio.dispose()를 확실하게 사용하려면 아래와 같이 하시면 됩니다.
laserSound = audio.loadSound( "laserSound.wav" )
backgroundMusic = audio.loadStream( "backgroundMusic.m4a" )
audio.dispose( laserSound )
audio.dispose( backgroundMusic )
laserSound = nil  -- This makes sure we can't use the handle again
backgroundMusic = nil  -- This makes sure we can't use the handle again

저 사운드를 게임의 스테이지 1에서만 사용하고 그 위 단계에서는 사용하지 않는다면 저렇게 메모리에서 완전히 사라지게 하는것도 좋은 방법이겠죠? 메모리 관리와 퍼포먼스 차원에서요......

audioPlayFrequency를 설정할 수도 있습니다.

config.lua 파일에 아래와 같이 audioPlayFrequency를 설정합니다.
  application =
  {
      content =
      {
          width = 320,
          height = 480,
          scale = "letterbox",
          audioPlayFrequency = 22050
      },
  }
22050Hz이상이 필요하지 않으면 위와 같이 하면 됩니다. 이것보다 높은 Hz가 필요하다면 44100으로 세팅을 하시구요.
코로나에서 제공되는 값은 11025,22050,44100Hz라고 합니다. 그 이외의 것들은 아직 테스트를 해보지 못했다고 하네요.

stereo가 아니라 mono sound를 사용하게 되면 메모리 점유 공간을 반으로 줄일 수 있습니다. 코로나에서는 OpenAL을 사용한다고 하는데요. OpenAL은 mono sound에 대해서 spatialized/3D effects를 제공한다고 합니다. 하지만 스테레오에는 이 3D효과를 제공하지 않는다네요. 오디오 쪽은 제가 잘 몰라서 이 3D 효과가 뭔지는 모르겠습니다. (돌비 서라운드 시스템 지원을 말하나???) 하여간 코로나에서는 아직까지 이 3D 효과는 지원하지 않고 있답니다.

audio.reserveChannels()
오디오를 로딩하고 플레이를 하면 코로나는 자동적으로 특정 채널에 이 오디오를 할당하게 됩니다.
그런데 이 채널에 볼륨이라던지 다른 세팅이 돼 있다면 그리고 1번 오디오를 플레이하고 2,3,4 번이 다른 채널에서 플레이 되다가 5번채널을 플레이 했는데 이것이 1번 오디오를 플레이했던 채널이랑 동일한 채널에 할당 된다면.
미리 그 채널에 맞춰놨던 세팅값이 5번 사운드에도 적용이 될 겁니다.

이런 부분 까지 제어 해야 될 필요성이 있다면 이 함수를 씁니다.
audio.reserveChannels(2) 이런식으로요. 그러면 해당 채널은 자동 할당이 안 되겠죠.

그리고 audio.findFreeChannel()함수도 있는데요.
이것은 해당 채널부터 할당할 채널을 검색하게하는 함수입니다.

local availableChannel = audio.findFreeChannel()
audio.play( laserSound, { channel=availableChannel } )

이렇게 하면 이 가능한 채널보다 높은 숫자의 채널들을 검색해서 할당하게 될 겁니다.

이상 코로나에서의 사운드 컨트롤에 대해서 알아보았습니다.
다음 시간엔 비디오, 카메라, 사진 라이브러리에 대해서 알아보겠습니다.

그리고 이런 미디어를 다룰 때 유용하게 사용할 수 있는 함수들을 알아보겠습니다.
예를 들어 반복 실행, 딜레이, Completion Listener 로 완료 후 어떤 동작 할 수 있도록 하기, 원래 실행 시간보다 길게 실행 되도록 하기, 잠깐 멈추기, 볼륨 조절 등등이요.

그럼 다음에 뵙겠습니다.




반응형