1

Is it possible/Is there a way to save an audio stream to a local file? I'm very inexperienced and I dont know where to start -- a point in the right direction would be appreciated.

I'm building an app that streams audio from URLs (eg http://audiostream.mp3). I'd like the user to be able to save audio, to a file, from when they start listening to when they finish. As the audio will be a stream, my concern is that i'm not dealing with a complete mp3 file -- i'm guessing this makes things trickier than simply downloading a complete mp3.

Thanks in advance for your help with this.

Baz
  • 41
  • 5
  • If you can get the whole file, you should be able to [trim it](http://stackoverflow.com/a/10778450/2710486). If you're building a streaming app, you should have the ability to decode and save it as mp3. – zc246 Dec 24 '15 at 13:41
  • Awesome -- thank you. I'll have a read and report back if i have any questions :) – Baz Dec 24 '15 at 13:45
  • Hi -- i'm still playing around with this method. Need to try and thrash out a few issues first... but i may need to come back to you. – Baz Dec 26 '15 at 13:59
  • This method seems to throw an error :(. – Baz Dec 26 '15 at 21:01
  • you need to have full understanding of audio streaming before going to these further requirements. – zc246 Dec 26 '15 at 21:57
  • 1
    Hi -- sorry, I dont understand what you mean. I dont have a full understanding, that's why i'm asking for guidance ;). Is this something you can help me with? – Baz Dec 27 '15 at 21:24

2 Answers2

3

You can save live audio stream with indefinite duration. You need to use ResourceLoader and URlSessionDataTask.

It's a bit tricky process, so I will explain step by step:

  1. Making an AVURLAsset:

    let asset = AVURLAsset(url: urlWithCustomScheme)
    
  2. Set the delegate:

    asset.resourceLoader.setDelegate(resourceLoaderDelegate, queue: DispatchQueue.main)
    
  3. Following delegate function will be called:

    func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool
    
  4. Now inside this function we will call a function to start the data request, so that downloading of sound buffer can start:

    func startDataRequest(with url: URL) {
        var recordingName = "default.mp3"
        if let recording = owner?.recordingName{
            recordingName = recording
        }
        fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
            .appendingPathComponent(recordingName)
        let configuration = URLSessionConfiguration.default
        configuration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData
        session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
        session?.dataTask(with: url).resume()
        outputStream = OutputStream(url: fileURL, append: true)
        outputStream?.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
        outputStream?.open()
    }
    

    as you can see we have opened a output stream. We will use this stream to write buffer to our disk.

  5. Now as we initiated a dataTask with the given URL, we will start receiving data bytes into the delegate function:

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
    
  6. Now we have started receiving live audio stream. Last piece of the puzzle is to store the audio stream. It is the simplest part. We just create an OutputStream object, open it then append the bytes we are receiving in the above delegate function and thats it, we saved the desired part of the live audio stream.

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        let bytesWritten = data.withUnsafeBytes{outputStream?.write($0, maxLength: data.count)}
        print("bytes written :\(bytesWritten!) to \(fileURL)")
    }
    

Here is the source code that I wrote on a blog in Medium, solving the problem that you faced: https://github.com/pandey-mohan/LiveAudioCapture

mohan
  • 76
  • 5
1

You can save the buffered song at the same time when it is played.

  NSData *aData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://www.noiseaddicts.com/samples_1w72b820/274.mp3"]];
    NSError *aError;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"/MyFolder.mp3"];
    NSLog(@"%@",dataPath);
    [aData writeToFile:dataPath atomically:YES];

    aPlayer = [[AVAudioPlayer alloc]initWithData:aData error:&aError];
    if (aError) {
        NSLog(@"Erri %@",aError.description);
    }
    aPlayer.volume = 1;
    [aPlayer play];
Rahul Mathur
  • 872
  • 1
  • 7
  • 20
  • Hi -- i couldn't get this to work. The code sticks on line one (NSData) when using my url. I think it's because the URL is a stream (like a radio broadcast) rather than a complete file (eg a song). What are your thoughts? – Baz Dec 26 '15 at 13:59
  • What is the size of your audio file? – Rahul Mathur Dec 28 '15 at 04:39
  • The URL is an audio stream; therefore i guess it gets bigger the longer you access it. – Baz Dec 29 '15 at 21:07