Есть ли способ записать аудиопоток, используя аудиопоток Мэтта Галлахера? - PullRequest
1 голос
/ 26 декабря 2010

Я использую Аудиопоток Мэтта Галлахера для потоковых радиостанций. Но как записать аудио? Есть ли способ загрузить загруженные пакеты в NSData и сохранить их в аудиофайл в папке документов на iPhone?

Спасибо

Ответы [ 3 ]

1 голос
/ 14 апреля 2013

Да, есть, и я сделал это. Моя проблема в том, что я могу воспроизвести его в одном и том же стримере (спрашивается в другом месте). Он будет воспроизводиться с помощью стандартного AVAudioPlayer в iOS. Однако это сохранит данные в файл, записав их в коде стримера.

В этом примере пропущены некоторые проверки ошибок, но вы получите основную идею.

Во-первых, звонок из основного потока, чтобы начать и остановить запись. Это в моем viewController, когда кто-то нажимает запись:

//---------------------------------------------------------
// Record button was pressed (toggle on/off)
// writes a file to the documents directory using date and time for the name
//---------------------------------------------------------

-(IBAction)recordButton:(id)sender {
    // only start if the streamer is playing (self.streamer is my streamer instance)
    if ([self.streamer isPlaying])     {

    NSDate *currentDateTime = [NSDate date];      // get current date and time                                         
    NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];      
    [dateFormatter setDateFormat:@"EEEE MMMM d YYYY 'at' HH:mm:ss"];
    NSString *dateString = [dateFormatter stringFromDate:currentDateTime];

    self.isRecording = !self.isRecording;       // toggle recording state BOOL
    if (self.isRecording)
    {
        // start recording here
        // change the record button to show it is recording - this is an IBOutlet
        [self.recordButtonImage setImage:[UIImage imageNamed:@"Record2.png"] forState:0];
       // call AudioStreamer to start recording. It returns the file pointer back
       // 
       self.recordFilePath = [self.streamer recordStream:TRUE fileName:dateString];    // start file stream and get file pointer              
    } else
    {
        //stop recording here
        // change the button back
        [self.recordButtonImage setImage:[UIImage imageNamed:@"Record.png"] forState:0];
        // call streamer code, stop the recording. Also returns the file path again.
        self.recordFilePath = [self.streamer recordStream:FALSE fileName:nil];     // stop stream and get file pointer             
        // add to "recorded files" for selecting a recorderd file later.
        // first, add channel, date, time
        dateString = [NSString stringWithFormat:@"%@ Recorded on %@",self.model.stationName, dateString];  // used to identify the item in a list laster
        // the dictionary will be used to hold the data on this recording for display elsewhere
        NSDictionary *row1 = [[[NSDictionary alloc] initWithObjectsAndKeys: self.recordFilePath, @"path", dateString, @"dateTime", nil] autorelease];
        // save the stream info in an array of recorded Streams
        if (self.model.recordedStreamsArray == nil) {
            self.model.recordedStreamsArray = [[NSMutableArray alloc] init]// init the array
        }
        [self.model.recordedStreamsArray addObject:row1];          // dict for this recording
        }
    }
}

СЕЙЧАС, в AudioStreamer.m мне нужно обработать вызов настройки записи выше

- (NSString*)recordStream:(BOOL)record fileName:(NSString *)fileName
{
    // this will start/stop recording, and return the file pointer
    if (record) {
        if (state == AS_PLAYING)
        {
            // now open a file to save the data into
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,       NSUserDomainMask, YES);
            NSString *documentsDirectory = [paths objectAtIndex:0];
            // will call this an mp3 file for now (this may need to change)
            NSMutableString *temp = [NSMutableString stringWithString:[documentsDirectory stringByAppendingFormat:@"/%@.mp3",fileName]];
            // remove the ':' in the time string, and create a file name w/ time & date
           [temp replaceOccurrencesOfString:@":" withString:@"" options:NSLiteralSearch range:NSMakeRange(0, [temp length])];

           self.filePath = temp;              // file name is date time generated.
            NSLog(@"Stream Save File Open = %@", self.filePath);
            // open the recording file stream output
            self.fileStream = [NSOutputStream outputStreamToFileAtPath:self.filePath append:NO];
           [self.fileStream open];
           NSLog(@"recording to %@", self.fileStream);
           self.isRecording = TRUE;
           return (self.filePath);             // if started, send back the file path
        }
      return (nil);                           // if not started, return nil for error checking
} else {
    // save the stream here to a file.
    // we are done, close the stream.
        if (self.fileStream != nil) {
            [self.fileStream close];
            self.fileStream = nil;
        }
        NSLog(@"stop recording");
        self.isRecording = FALSE;
        return (self.filePath);                       // when stopping, return nil
    }
}

ПОСЛЕДНЕЕ, нам нужно изменить часть данных стримера, чтобы фактически сохранить байты. Вам необходимо изменить код потока в методе: - (void) handleReadFromStream: (CFReadStreamRef) aStreameventType: (CFStreamEventType) eventType Прокрутите этот метод вниз, пока не найдете:

@synchronized(self)
{
    if ([self isFinishing] || !CFReadStreamHasBytesAvailable(stream))
    {
        return;
    }

    //
    // Read the bytes from the stream
    //
    length = CFReadStreamRead(stream, bytes, kAQDefaultBufSize);

    if (length == -1)
    {
        [self failWithErrorCode:AS_AUDIO_DATA_NOT_FOUND];
        return;
    }

ПРАВО после длины = строки, добавьте следующий код:

            //
            // if recording, save the raw data to a file
            //
            if(self.isRecording && length != 0){
                //
                // write the data to a file
                //
                NSInteger       bytesWritten;
                NSInteger       bytesWrittenSoFar;
                bytesWrittenSoFar = 0;
                do {
                    bytesWritten = [self.fileStream write:&bytes[bytesWrittenSoFar] maxLength:length - bytesWrittenSoFar];
                    NSLog(@"bytesWritten = %i",bytesWritten);
                    if (bytesWritten == -1) {
                        [self.fileStream close];
                        self.fileStream = nil;
                        NSLog(@"File write error");
                        break;
                    } else {
                        bytesWrittenSoFar += bytesWritten;
                    }
                } while (bytesWrittenSoFar != length);
            }

Вот декларации .h:

Добавлен интерфейс для AudioStreamer.h

// for recording and saving a stream
NSString*        filePath;
NSOutputStream*  fileStream;
BOOL isRecording;
BOOL isPlayingFile;

В вашем контроллере представления вам понадобится:

@property(nonatomic, assign) IBOutlet UIButton* recordButtonImage;
@property(nonatomic, assign) BOOL isRecording;
@property (nonatomic, copy)   NSString* recordFilePath;

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

Кроме того, кто-то спросил о self.model.xxx. Модель - это объект данных, который я создал, чтобы позволить мне легко передавать данные, которые используются более чем одним объектом, а также изменяются более чем одним объектом. Я знаю, что глобальные данные - это плохая форма, но бывают случаи, когда доступ к ним становится проще. Я передаю модель данных каждому новому объекту при вызове. Я сохраняю массив каналов, название песни, имя исполнителя и другие данные, связанные с потоком, внутри модели. Я также помещаю любые данные, которые я хочу сохранить через запуски, например настройки, и записываю эту модель данных в файл каждый раз, когда меняются постоянные данные. В этом примере вы можете хранить данные локально. Если вам нужна помощь в прохождении модели, дайте мне знать.

0 голосов
/ 22 августа 2015

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

В Git Hub вы можете найти это использование Этот класс очень прост в использовании

0 голосов
/ 16 августа 2013

ОК, вот как я воспроизводю записанный файл. При воспроизведении файла URL станции содержит путь к файлу. self.model.playRecordedSong содержит значение времени, сколько секунд в потоке я хочу воспроизвести. Я держу словарь названия песни и индекса времени, чтобы я мог перейти к записанному потоку в начале любой песни. Используйте 0, чтобы начать с начала.

  NSError *error;
    NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:self.model.stationURL, [[NSBundle mainBundle] resourcePath]]];
    // get the file URL  and then create an audio player if we don't already have one.
    if (audioPlayer == nil) {
        // set the seconds count to the proper start point (0, or some time into the stream)
        // this will be 0 for start of stream, or some value passed back if they picked a song.
        self.recordPlaySecondsCount = self.model.playRecordedSong;
        //create a new player
        audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
        // set self so we can catch the end of file.
        [audioPlayer setDelegate: self];
        // audio player needs an NSTimeInterval. Get it from the seconds start point.
        NSTimeInterval interval = self.model.playRecordedSong;
        // seek to the proper place in file.
        audioPlayer.currentTime = interval;
    }
    audioPlayer.numberOfLoops = 0;          // do not repeat

    if (audioPlayer == nil)
        NSLog(@"AVAudiolayer error: %@", error);
    // I need to do more on the error of no player
    else {
        [audioPlayer play];
    }

Надеюсь, это поможет вам воспроизвести записанный файл.

...